多线程

xiaoxiao2021-02-28  105

主要用threading模块 1 Thread 普通创建

#! /user/bin/env python #encoding=utf-8 __author__ = 'chw' import threading import time def run(n): print ("start:"+n) # time.sleep(3) print ("end:"+n) if __name__=="__main__": t1=threading.Thread(target=run,args=("t1",)) t2=threading.Thread(target=run,args=("t2",)) t1.start() t2.start()

结果

start:t1 end:t1 start:t2 end:t2

继承线程类threading.Thread() 本质是对重新写run()函数

class Mythread(threading.Thread): def __init__(self,n): super(Mythread,self).__init__()# 重构run函数必须要写 self.n=n def run(self): print "start:"+self.n time.sleep(3) print "end:"+self.n+'\n' if __name__=="__main__": t1=Mythread("t1") t2=Mythread("t2") t1.start() t2.start()

结果

start:t1 start:t2 end:t1 end:t2

threading.Thread.join() #等此线程执行完后,再执行其他线程或主线程 threading.current_thread() #输出当前线程 threading.active_count() #当前活动的进程数 由于主线程比子线程慢很多,当主线程执行active_count()时,其他子线程都已经执行完毕,因此利用主线程统计的活跃的线程数num = 1(主线程本身) threading.setDaemon(True)#守护进程 把子进程设置为守护线程,必须在start()之前设置 把所有的子线程都变成了主线程的守护线程,因此当主进程结束后,子线程也会随之结束。所以当主线程结束后,整个程序就退出了。

#! /user/bin/env python #encoding=utf-8 __author__ = 'chw' import threading import time class Mythread(threading.Thread): def __init__(self,n): super(Mythread,self).__init__()#重写run self.n=n def run(self): print "task"+str(self.n)+str(threading.currentThread()) time.sleep(3) print "end:"+str(self.n) if __name__=="__main__": start_time = time.time() t_obj = [] # 定义列表用于存放子线程实例 for i in xrange(3): t=Mythread(i) t.start() t_obj.append(t) print threading.activeCount() for j in t_obj: j.join() print "cost:"+str(time.time()-start_time) print threading.currentThread() task0<Mythread(Thread-1, started 4488)> task1<Mythread(Thread-2, started 708)> task2<Mythread(Thread-3, started 5688)> 4 end:0 end:1end:2 cost:3.00600004196

守护进程

#! /user/bin/env python #encoding=utf-8 __author__ = 'chw' import threading import time class Mythread(threading.Thread): def __init__(self,n): super(Mythread,self).__init__()#重写run self.n=n def run(self): print "task"+str(self.n)+str(threading.currentThread()) time.sleep(3) print "end:"+str(self.n) if __name__=="__main__": start_time = time.time() t_obj = [] # 定义列表用于存放子线程实例 for i in xrange(3): t=Mythread(i) t.setDaemon(True) t.start() t_obj.append(t) print threading.activeCount() # for j in t_obj: # j.join() print "cost:"+str(time.time()-start_time) print threading.currentThread()#当前运行的线程 task0<Mythread(Thread-1, started daemon 2416)> task1<Mythread(Thread-2, started daemon 3648)> task2<Mythread(Thread-3, started daemon 724)> 4 cost:0.000999927520752 <_MainThread(MainThread, started 5948)>

GIL 在非python环境中,单核情况下,同时只能有一个任务执行。多核时可以支持多个线程同时执行。但是在python中,无论有多少核,同时只能执行一个线程。究其原因,这就是由于GIL的存在导致的。

GIL的全称是Global Interpreter Lock(全局解释器锁),来源是python设计之初的考虑,为了数据安全所做的决定。某个线程想要执行,必须先拿到GIL,我们可以把GIL看作是“通行证”,并且在一个python进程中,GIL只有一个。拿不到通行证的线程,就不允许进入CPU执行。GIL只在cpython中才有,因为cpython调用的是c语言的原生线程,所以他不能直接操作cpu,只能利用GIL保证同一时间只能有一个线程拿到数据。而在pypy和jpython中是没有GIL的。

Python多线程的工作过程: python在使用多线程的时候,调用的是c语言的原生线程。 1.拿到公共数据 2.申请gil 3.python解释器调用os原生线程 4.os操作cpu执行运算 5.当该线程执行时间到后,无论运算是否已经执行完,gil都被要求释放 6.进而由其他进程重复上面的过程 7.等其他进程执行完后,又会切换到之前的线程(从他记录的上下文继续执行) 整个过程是每个线程执行自己的运算,当执行时间到就进行切换(context switch)。 • python针对不同类型的代码执行效率也是不同的: 1、CPU密集型代码(各种循环处理、计算等等),在这种情况下,由于计算工作多,ticks计数很快就会达到阈值,然后触发GIL的释放与再竞争(多个线程来回切换当然是需要消耗资源的),所以python下的多线程对CPU密集型代码并不友好。 2、IO密集型代码(文件处理、网络爬虫等涉及文件读写的操作),多线程能够有效提升效率(单线程下有IO操作会进行IO等待,造成不必要的时间浪费,而开启多线程能在线程A等待时,自动切换到线程B,可以不浪费CPU的资源,从而能提升程序执行效率)。所以python的多线程对IO密集型代码比较友好。 使用建议 python下想要充分利用多核CPU,就用多进程。因为每个进程有各自独立的GIL,互不干扰,这样就可以真正意义上的并行执行,在python中,多进程的执行效率优于多线程(仅仅针对多核CPU而言)。 GIL在python中的版本差异: 1、在python2.x里,GIL的释放逻辑是当前线程遇见IO操作或者ticks计数达到100时进行释放。(ticks可以看作是python自身的一个计数器,专门做用于GIL,每次释放后归零,这个计数可以通过sys.setcheckinterval 来调整)。而每次释放GIL锁,线程进行锁竞争、切换线程,会消耗资源。并且由于GIL锁存在,python里一个进程永远只能同时执行一个线程(拿到GIL的线程才能执行),这就是为什么在多核CPU上,python的多线程效率并不高。 2、在python3.x中,GIL不使用ticks计数,改为使用计时器(执行时间达到阈值后,当前线程释放GIL),这样对CPU密集型程序更加友好,但依然没有解决GIL导致的同一时间只能执行一个线程的问题,所以效率依然不尽如人意。 线程锁 由于线程之间是进行随机调度,并且每个线程可能只执行n条执行之后,当多个线程同时修改同一条数据时可能会出现脏数据,所以,出现了线程锁,即同一时刻允许一个线程执行操作。线程锁用于锁定资源,你可以定义多个锁, 像下面的代码, 当你需要独占某一资源时,任何一个锁都可以锁这个资源,就好比你用不同的锁都可以把相同的一个门锁住是一个道理。

由于线程之间是进行随机调度,如果有多个线程同时操作一个对象,如果没有很好地保护该对象,会造成程序结果的不可预期,我们也称此为“线程不安全”。

#! /user/bin/env python #encoding=utf-8 __author__ = 'chw' import threading import time def run(n): global num num+=1 if __name__=="__main__": num=0 t_obj=[] for i in xrange(10): t=threading.Thread(target=run,args=(i,)) t.start() t_obj.append(t) for t in t_obj: t.join() print "num:"+str(num)

结果

num:10

互斥锁

#! /user/bin/env python #encoding=utf-8 __author__ = 'chw' import threading import time def run(n): # with lock: # global num # num+=1 lock.acquire() global num num += 1 lock.release() if __name__=="__main__": lock = threading.Lock() num=0 t_obj=[] for i in xrange(10): t=threading.Thread(target=run,args=(i,)) t.start() t_obj.append(t) for t in t_obj: t.join() print "num:"+str(num)

结果

num:10

递归锁 RLcok类的用法和Lock类一模一样,但它支持嵌套,,在多个锁没有释放的时候一般会使用使用RLcok类。

#! /user/bin/env python #encoding=utf-8 __author__ = 'chw' import threading import time def run(n): # with lock: # global num # num+=1 lock.acquire() global num num += 1 lock.release() if __name__=="__main__": lock = threading.RLock() num=0 t_obj=[] for i in xrange(10): t=threading.Thread(target=run,args=(i,)) t.start() t_obj.append(t) for t in t_obj: t.join() print "num:"+str(num)

信号量(BoundedSemaphore类) 互斥锁同时只允许一个线程更改数据,而Semaphore是同时允许一定数量的线程更改数据 ,比如厕所有3个坑,那最多只允许3个人上厕所,后面的人只能等里面有人出来了才能再进去。

#! /user/bin/env python #encoding=utf-8 __author__ = 'chw' import threading import time def run(n): semaphore.acquire()#加锁 time.sleep(1) print ("run the thread:%s\n" % threading.current_thread()) semaphore.release() if __name__=="__main__": semaphore = threading.Semaphore(5)#最多5个进程同时接入 num=0 for i in xrange(10): t=threading.Thread(target=run,args=(i,)) t.start() if threading.active_count()!=1: pass else: print "-----all threads done"

结果:

run the thread:<Thread(Thread-1, started 5048)> run the thread:<Thread(Thread-2, started 3924)> run the thread:<Thread(Thread-5, started 4920)> run the thread:<Thread(Thread-3, started 4272)> run the thread:<Thread(Thread-4, started 3436)> run the thread:<Thread(Thread-6, started 4472)> run the thread:<Thread(Thread-7, started 5212)> run the thread:<Thread(Thread-8, started 2268)> run the thread:<Thread(Thread-9, started 5468)> run the thread:<Thread(Thread-10, started 6052)>

Event 事件处理的机制:全局定义了一个“Flag”,当flag值为“False”,那么event.wait()就会阻塞,当flag值为“True”,那么event.wait()便不再阻塞。 set():将flag设置为“True” clear():将flag设置为“False” wait():判断是否设置“flag” is_set():会一直监听flag,如果没有检测到flag就一直处于阻塞状态 事件处理的机制:全局定义了一个“Flag”,当flag值为“False”,那么event.wait()就会阻塞,当flag值为“True”,那么event.wait()便不再阻塞。

#! /user/bin/env python #encoding=utf-8 __author__ = 'chw' #利用Event类模拟红绿灯 import threading import time def lighter(): count=0 event.set()#初始化为绿灯 while True: if 5<count<10: event.clear()#红灯,清除标识位 print ("\33[41;1mred light is on...\033[0m") elif count>10: event.set()#绿灯,设置标志位 count=0 else: print("\33[42;1mgreen light is on...\033[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) if __name__=="__main__": event = threading.Event() light=threading.Thread(target=lighter,) light.start() car=threading.Thread(target=car,args=("mini",)) car.start()

部分结果:

green light is on... [mini] running... green light is on... [mini] running... green light is on...[mini] running... [mini] running... green light is on... [mini] running... green light is on... [mini] running... green light is on... [mini] running...red light is on...

条件(Condition类) 使得线程等待,只有满足某条件时,才释放n个线程 定时器(Timer类) 定时器,指定n秒后执行某操作

from threading import Timer def hello(): print("hello, world") t = Timer(1, hello) t.start()

after 1 seconds, “hello, world” will be printed

参考:http://www.cnblogs.com/whatisfantasy/p/6440585.html

转载请注明原文地址: https://www.6miu.com/read-56918.html

最新回复(0)