资讯专栏INFORMATION COLUMN

Python中过程threading.Thread的应用详细说明

89542767 / 232人阅读

  1.过程这个概念


  过程,有时候被称作轻量级进程(LightweightProcess,LWP),是程序执行流的最低控制模块。统一标准的过程由过程ID,现阶段命令表针(PC),存储器结合和局部变量构成。此外,过程是过程里的一个实体线,被系统软件多带带生产调度和分配的基本要素,过程自己并不有着服务器资源。


  2.threading.thread()简单地应用


  2.1加上过程能是程序执行迅速


  pythod的thread模块还是比较最底层的控制模块,pythod的threading模块是对thread做了很多包装,能够方便快捷被应用。


  有一点在运行时不可缺少的网络资源,但是它能与同为1个进度的其他线程共享过程所具有的所有网络资源。


  import threading
  import time
  def saySorry():
  print("亲爱的,我错了,我能吃饭了吗?")
  time.sleep(5)
  if __name__=="__main__":
  start_time1=time.time()
  for i in range(5):
  t=threading.Thread(target=saySorry)
  t.start()#启动线程,即让线程开始执行
  end_time1=time.time()
  print(end_time1-start_time1)
  start_time2=time.time()
  for i in range(5):
  t=saySorry()
  end_time2=time.time()
  print(end_time2-start_time2)


  输出为:


  亲爱的,我错了,我能吃饭了吗?


  亲爱的,我错了,我能吃饭了吗?


  亲爱的,我错了,我能吃饭了吗?


  亲爱的,我错了,我能吃饭了吗?


  亲爱的,我错了,我能吃饭了吗?


  0.001995086669921875


  亲爱的,我错了,我能吃饭了吗?


  亲爱的,我错了,我能吃饭了吗?


  亲爱的,我错了,我能吃饭了吗?


  亲爱的,我错了,我能吃饭了吗?


  亲爱的,我错了,我能吃饭了吗?


  25.001766204833984


  2.2主线程会等待所有的子线程结束后才结束


  import threading
  from time import sleep,ctime
  def sing():
  for i in range(3):
  print("正在唱歌...%d"%i)
  sleep(1)
  def dance():
  for i in range(3):
  print("正在跳舞...%d"%i)
  sleep(1)
  if __name__=='__main__':
  print('---开始---:%s'%ctime())
  t1=threading.Thread(target=sing)
  t2=threading.Thread(target=dance)
  t1.start()
  t2.start()
  #sleep(5)#屏蔽此行代码,试试看,程序是否会立马结束?
  print('---结束---:%s'%ctime())


  输出为:


  ---开始---:Mon Sep 28 14:42:09 2020


  正在唱歌...0


  正在跳舞...0---结束---:Mon Sep 28 14:42:09 2020


  正在唱歌...1


  正在跳舞...1


  正在唱歌...2


  正在跳舞...2


  如果释放‘sleep(5)’,输出为:


  ---开始---:Mon Sep 28 14:43:36 2020


  正在唱歌...0


  正在跳舞...0


  正在跳舞...1


  正在唱歌...1


  正在唱歌...2正在跳舞...2


  ---结束---:Mon Sep 28 14:43:41 2020


  3.查看线程数量


  import threading
  from time import sleep,ctime
  def sing():
  for i in range(3):
  print("正在唱歌...%d"%i)
  sleep(1)
  def dance():
  for i in range(3):
  print("正在跳舞...%d"%i)
  sleep(1)
  if __name__=='__main__':
  print('---开始---:%s'%ctime())
  t1=threading.Thread(target=sing)
  t2=threading.Thread(target=dance)
  t1.start()
  t2.start()
  while True:
  length=len(threading.enumerate())
  print('当前运行的线程数为:%d'%length)
  if length<=1:
  break
  sleep(0.5)


  输出为:


  ---开始---:Mon Sep 28 14:46:16 2020


  正在唱歌...0


  正在跳舞...0


  当前运行的线程数为:3


  当前运行的线程数为:3


  正在唱歌...1


  正在跳舞...1当前运行的线程数为:3


  当前运行的线程数为:3


  正在唱歌...2


  正在跳舞...2


  当前运行的线程数为:3


  当前运行的线程数为:3


  当前运行的线程数为:1


  4.线程参数及顺序


  4.1传递参数的方法


  使用args传递参数threading.Thread(target=sing,args=(10,100,100))


  使用kwargs传递参数threading.Thread(target=sing,kwargs={“a”:10,“b”:100,“c”:100})


  同时使用args和kwargs传递参数threading.Thread(target=sing,args=(10,),kwargs={“b”:100,“c”:100})


  4.2线程的执行顺序


  import threading
  import time
  def sing():
  for i in range(5):
  print("我是sing")
  time.sleep(1)
  def dance():
  for i in range(5):
  print("我是dance")
  time.sleep(1)
  if __name__=='__main__':
  #创建两个子线程
  t1=threading.Thread(target=sing)
  t2=threading.Thread(target=dance)
  #启动子线程
  t1.start()
  t2.start()


  输出为:


  我是sing


  我是dance


  我是sing


  我是dance


  我是dance


  我是sing


  我是dance我是sing


  我是sing


  我是dance


  说明:


  从代码和执行结果我们可以看出,多线程程序的执行顺序是不确定的。当执行到sleep语句时,线程将被阻塞(Blocked),到sleep结束后,线程进入就绪(Runnable)状态,等待调度。而线程调度将自行选择一个线程执行。上面的代码中只能保证每个线程都运行完整个run函数,但是线程的启动顺序、run函数中每次循环的执行顺序都不能确定。


  5.守护线程


  守护线程:如果在程序中将子线程设置为守护线程,则该子线程会在主线程结束时自动退出,设置方式为thread.setDaemon(True),要在thread.start()之前设置,默认是false的,也就是主线程结束时,子线程依然在执行。


  5.1如下代码,主线程已经exit()【其实并没有真正结束】,子线程还在继续执行


  import threading
  import time
  def test():
  for i in range(7):
  print("test is run:",i)
  time.sleep(1)
  if __name__=='__main__':
  #创建子线程
  t1=threading.Thread(target=test)
  #启动子线程
  t1.start()
  #休眠2秒
  time.sleep(2)
  print("我OVER了")
  #退出
  exit()


  输出为:


  test is run:0


  test is run:1


  我OVER了


  test is run:2


  test is run:3


  test is run:4


  test is run:5


  test is run:6


  5.2设置守护线程


  为线程设置守护,如果主线程结束,子线程也随之结束。


  import threading
  import time
  def test():
  for i in range(7):
  print("test is run:",i)
  time.sleep(1)
  if __name__=='__main__':
  #创建子线程
  t1=threading.Thread(target=test)
  #设置线程保护
  t1.setDaemon(True)
  #启动子线程
  t1.start()
  #休眠2秒
  time.sleep(2)
  print("我OVER了")
  #退出
  exit()
  输出为:
  test is run:0
  test is run:1
  我OVER了
  参考代码
  import threading
  from threading import Lock,Thread
  import time,os
  '''


  python多线程详解


  什么是线程?


  线程也叫轻量级进程,是操作系统能够进行运算调度的最小单位,它被包涵在进程之中,是进程中的实际运作单位。


  线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其他线程共享进程所


  拥有的全部资源。一个线程可以创建和撤销另一个线程,同一个进程中的多个线程之间可以并发执行


  '''


  '''


  为什么要使用多线程?


  线程在程序中是独立的、并发的执行流。与分隔的进程相比,进程中线程之间的隔离程度要小,它们共享内存、文件句柄


  和其他进程应有的状态。


  因为线程的划分尺度小于进程,使得多线程程序的并发性高。进程在执行过程之中拥有独立的内存单元,而多个线程共享


  内存,从而极大的提升了程序的运行效率。


  线程比进程具有更高的性能,这是由于同一个进程中的线程都有共性,多个线程共享一个进程的虚拟空间。线程的共享环境


  包括进程代码段、进程的共有数据等,利用这些共享的数据,线程之间很容易实现通信。


  操作系统在创建进程时,必须为改进程分配独立的内存空间,并分配大量的相关资源,但创建线程则简单得多。因此,使用多线程


  来实现并发比使用多进程的性能高得要多。


  '''


  '''


  总结起来,使用多线程编程具有如下几个优点:


  进程之间不能共享内存,但线程之间共享内存非常容易。


  操作系统在创建进程时,需要为该进程重新分配系统资源,但创建线程的代价则小得多。因此使用多线程来实现多任务并发执行比使用多进程的效率高


  python语言内置了多线程功能支持,而不是单纯地作为底层操作系统的调度方式,从而简化了python的多线程编程。


  '''
  '''
  普通创建方式
  '''
  #def run(n):
  #print('task',n)
  #time.sleep(1)
  #print('2s')
  #time.sleep(1)
  #print('1s')
  #time.sleep(1)
  #print('0s')
  #time.sleep(1)
  #
  #if __name__=='__main__':
  #t1=threading.Thread(target=run,args=('t1',))#target是要执行的函数名(不是函数),args是函数对应的参数,以元组的形式存在
  #t2=threading.Thread(target=run,args=('t2',))
  #t1.start()
  #t2.start()
  '''
  自定义线程:继承threading.Thread来定义线程类,其本质是重构Thread类中的run方法
  '''
  #class MyThread(threading.Thread):
  #def __init__(self,n):
  #super(MyThread,self).__init__()#重构run函数必须写
  #self.n=n
  #
  #def run(self):
  #print('task',self.n)
  #time.sleep(1)
  #print('2s')
  #time.sleep(1)
  #print('1s')
  #time.sleep(1)
  #print('0s')
  #time.sleep(1)
  #
  #if __name__=='__main__':
  #t1=MyThread('t1')
  #t2=MyThread('t2')
  #t1.start()
  #t2.start()
  '''


  守护线程


  下面这个例子,这里使用setDaemon(True)把所有的子线程都变成了主线程的守护线程,


  因此当主线程结束后,子线程也会随之结束,所以当主线程结束后,整个程序就退出了。


  所谓'线程守护',就是主线程不管该线程的执行情况,只要是其他子线程结束且主线程执行完毕,主线程都会关闭。也就是说:主线程不等待该守护线程的执行完再去关闭。


  '''
  #def run(n):
  #print('task',n)
  #time.sleep(1)
  #print('3s')
  #time.sleep(1)
  #print('2s')
  #time.sleep(1)
  #print('1s')
  #
  #if __name__=='__main__':
  #t=threading.Thread(target=run,args=('t1',))
  #t.setDaemon(True)
  #t.start()
  #print('end')
  '''
  通过执行结果可以看出,设置守护线程之后,当主线程结束时,子线程也将立即结束,不再执行
  '''
  '''
  主线程等待子线程结束
  为了让守护线程执行结束之后,主线程再结束,我们可以使用join方法,让主线程等待子线程执行
  '''
  #def run(n):
  #print('task',n)
  #time.sleep(2)
  #print('5s')
  #time.sleep(2)
  #print('3s')
  #time.sleep(2)
  #print('1s')
  #if __name__=='__main__':
  #t=threading.Thread(target=run,args=('t1',))
  #t.setDaemon(True)#把子线程设置为守护线程,必须在start()之前设置
  #t.start()
  #t.join()#设置主线程等待子线程结束
  #print('end')
  '''
  多线程共享全局变量
  线程时进程的执行单元,进程时系统分配资源的最小执行单位,所以在同一个进程中的多线程是共享资源的
  '''
  #g_num=100
  #def work1():
  #global g_num
  #for i in range(3):
  #g_num+=1
  #print('in work1 g_num is:%d'%g_num)
  #
  #def work2():
  #global g_num
  #print('in work2 g_num is:%d'%g_num)
  #
  #if __name__=='__main__':
  #t1=threading.Thread(target=work1)
  #t1.start()
  #time.sleep(1)
  #t2=threading.Thread(target=work2)
  #t2.start()
  '''
  由于线程之间是进行随机调度,并且每个线程可能只执行n条执行之后,当多个线程同时修改同一条数据时可能会出现脏数据,
  所以出现了线程锁,即同一时刻允许一个线程执行操作。线程锁用于锁定资源,可以定义多个锁,像下面的代码,当需要独占
  某一个资源时,任何一个锁都可以锁定这个资源,就好比你用不同的锁都可以把这个相同的门锁住一样。
  由于线程之间是进行随机调度的,如果有多个线程同时操作一个对象,如果没有很好地保护该对象,会造成程序结果的不可预期,
  我们因此也称为“线程不安全”。
  为了防止上面情况的发生,就出现了互斥锁(Lock)
  '''
  #def work():
  #global n
  #lock.acquire()
  #temp=n
  #time.sleep(0.1)
  #n=temp-1
  #lock.release()
  #
  #
  #if __name__=='__main__':
  #lock=Lock()
  #n=100
  #l=[]
  #for i in range(100):
  #p=Thread(target=work)
  #l.append(p)
  #p.start()
  #for p in l:
  #p.join()
  '''
  递归锁:RLcok类的用法和Lock类一模一样,但它支持嵌套,在多个锁没有释放的时候一般会使用RLock类
  '''
  #def func(lock):
  #global gl_num
  #lock.acquire()
  #gl_num+=1
  #time.sleep(1)
  #print(gl_num)
  #lock.release()
  #
  #
  #if __name__=='__main__':
  #gl_num=0
  #lock=threading.RLock()
  #for i in range(10):
  #t=threading.Thread(target=func,args=(lock,))
  #t.start()
  '''
  信号量(BoundedSemaphore类)
  互斥锁同时只允许一个线程更改数据,而Semaphore是同时允许一定数量的线程更改数据,比如厕所有3个坑,
  那最多只允许3个人上厕所,后面的人只能等里面有人出来了才能再进去
  '''
  #def run(n,semaphore):
  #semaphore.acquire()#加锁
  #time.sleep(3)
  #print('run the thread:%sn'%n)
  #semaphore.release()#释放
  #
  #
  #if __name__=='__main__':
  #num=0
  #semaphore=threading.BoundedSemaphore(5)#最多允许5个线程同时运行
  #for i in range(22):
  #t=threading.Thread(target=run,args=('t-%s'%i,semaphore))
  #t.start()
  #while threading.active_count()!=1:
  #pass
  #else:
  #print('----------all threads done-----------')
  '''
  python线程的事件用于主线程控制其他线程的执行,事件是一个简单的线程同步对象,其主要提供以下的几个方法:
  clear将flag设置为False
  set将flag设置为True
  is_set判断是否设置了flag
  wait会一直监听flag,如果没有检测到flag就一直处于阻塞状态
  事件处理的机制:全局定义了一个Flag,当Flag的值为False,那么event.wait()就会阻塞,当flag值为True,
  那么event.wait()便不再阻塞
  '''
  event=threading.Event()
  def lighter():
  count=0
  event.set()#初始者为绿灯
  while True:
  if 5<count<=10:
  event.clear()#红灯,清除标志位
  print("33[41;lmred light is on...33[0m]")
  elif count>10:
  event.set()#绿灯,设置标志位
  count=0
  else:
  print('33[42;lmgreen light is on...33[0m')
  time.sleep(1)
  count+=1
  def car(name):
  while True:
  if event.is_set():#判断是否设置了标志位
  print('[%s]running.....'%name)
  time.sleep(1)
  else:
  print('[%s]sees red light,waiting...'%name)
  event.wait()
  print('[%s]green light is on,start going...'%name)
  #startTime=time.time()
  light=threading.Thread(target=lighter,)
  light.start()
  car=threading.Thread(target=car,args=('MINT',))
  car.start()
  endTime=time.time()
  #print('用时:',endTime-startTime)
  '''


  GIL全局解释器


  在非python环境中,单核情况下,同时只能有一个任务执行。多核时可以支持多个线程同时执行。但是在python中,无论有多少个核


  同时只能执行一个线程。究其原因,这就是由于GIL的存在导致的。


  GIL的全程是全局解释器,来源是python设计之初的考虑,为了数据安全所做的决定。某个线程想要执行,必须先拿到GIL,我们可以


  把GIL看做是“通行证”,并且在一个python进程之中,GIL只有一个。拿不到线程的通行证,并且在一个python进程中,GIL只有一个,


  拿不到通行证的线程,就不允许进入CPU执行。GIL只在cpython中才有,因为cpython调用的是c语言的原生线程,所以他不能直接操


  作cpu,而只能利用GIL保证同一时间只能有一个线程拿到数据。而在pypy和jpython中是没有GIL的


  python在使用多线程的时候,调用的是c语言的原生过程。


  '''


  '''


  python针对不同类型的代码执行效率也是不同的


  1、CPU密集型代码(各种循环处理、计算等),在这种情况下,由于计算工作多,ticks技术很快就会达到阀值,然后出发GIL的


  释放与再竞争(多个线程来回切换当然是需要消耗资源的),所以python下的多线程对CPU密集型代码并不友好。


  2、IO密集型代码(文件处理、网络爬虫等设计文件读写操作),多线程能够有效提升效率(单线程下有IO操作会进行IO等待,


  造成不必要的时间浪费,而开启多线程能在线程A等待时,自动切换到线程B,可以不浪费CPU的资源,从而能提升程序的执行


  效率)。所以python的多线程对IO密集型代码比较友好。


  '''


  '''


  主要要看任务的类型,我们把任务分为I/O密集型和计算密集型,而多线程在切换中又分为I/O切换和时间切换。如果任务属于是I/O密集型,


  若不采用多线程,我们在进行I/O操作时,势必要等待前面一个I/O任务完成后面的I/O任务才能进行,在这个等待的过程中,CPU处于等待


  状态,这时如果采用多线程的话,刚好可以切换到进行另一个I/O任务。这样就刚好可以充分利用CPU避免CPU处于闲置状态,提高效率。但是


  如果多线程任务都是计算型,CPU会一直在进行工作,直到一定的时间后采取多线程时间切换的方式进行切换线程,此时CPU一直处于工作状态,


  此种情况下并不能提高性能,相反在切换多线程任务时,可能还会造成时间和资源的浪费,导致效能下降。这就是造成上面两种多线程结果不能的解释。


  结论:I/O密集型任务,建议采取多线程,还可以采用多进程+协程的方式(例如:爬虫多采用多线程处理爬取的数据);对于计算密集型任务,python此时就不适用了。


  '''


  综上所述,这篇文章就给大家介绍到这里了,希望可以给大家带来帮助。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/128697.html

相关文章

  • Python迅速从短视频获取视频帧办法详细说明

      本文给大家介绍一类从视频里获取视频帧的办法,因为单核获取视频帧速率比较慢,因而接下来我们增强了线程同步的办法,感兴趣的朋友能够出手试一试  Python迅速获取视频帧(线程同步)  现在详细介绍一下一类从视频里获取视频帧的办法,因为单核获取视频帧速率比较慢,因而接下来我们增强了线程同步的办法。  1、获取视频帧  获取视频帧关键用了Opencv控制模块。  在其中:  camera=cv2.V...

    89542767 评论0 收藏0
  • PyTips 0x 12 - Python 线程与协程(1)

    摘要:中关于线程的标准库是,之前在版本中的在之后更名为,无论是还是都应该尽量避免使用较为底层的而应该使用。而与线程相比,协程尤其是结合事件循环无论在编程模型还是语法上,看起来都是非常友好的单线程同步过程。 项目地址:https://git.io/pytips 要说到线程(Thread)与协程(Coroutine)似乎总是需要从并行(Parallelism)与并发(Concurrency)谈起...

    el09xccxy 评论0 收藏0
  • 浅谈Python多线程

    摘要:进程可创建多个线程来执行同一程序的不同部分。就绪等待线程调度。运行线程正常运行阻塞暂停运行,解除阻塞后进入状态重新等待调度。消亡线程方法执行完毕返回或者异常终止。多线程多的情况下,依次执行各线程的方法,前头一个结束了才能执行后面一个。 浅谈Python多线程 作者简介: 姓名:黄志成(小黄)博客: 博客 线程 一.什么是线程? 操作系统原理相关的书,基本都会提到一句很经典的话: 进程...

    zsirfs 评论0 收藏0
  • Python多线程

    摘要:多线程的理解多进程和多线程都可以执行多个任务,线程是进程的一部分。多线程创建在中,同样可以实现多线程,有两个标准模块和,不过我们主要使用更高级的模块。多线程的应用场景。 1、多线程的理解 多进程和多线程都可以执行多个任务,线程是进程的一部分。线程的特点是线程之间可以共享内存和变量,资源消耗少(不过在Unix环境中,多进程和多线程资源调度消耗差距不明显,Unix调度较快),缺点是线程之间...

    dcr309duan 评论0 收藏0
  • # Python 多线程和锁

    摘要:多线程和锁作者博客进程和线程进程是执行中的计算机程序。线程包括开始执行顺序和结束三部分。的多进程相关模块模块是高级别的多线程模块。线程锁当多线程争夺锁时,允许第一个获得锁的线程进入临街区,并执行代码。 Python 多线程和锁 作者博客:http://zzir.cn/ 进程和线程 进程是执行中的计算机程序。每个进程都拥有自己的地址空间、内存、数据栈及其它的辅助数据。操作系统管理着所有的...

    cpupro 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<