多线程
来源:互联网 发布:网络推广工作安排 编辑:程序博客网 时间:2024/06/11 03:19
主要用threading模块
1 Thread
普通创建
#! /user/bin/env python#encoding=utf-8__author__ = 'chw'import threadingimport timedef 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:t1end:t1start:t2end: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:t1start:t2end:t1end: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 threadingimport timeclass 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)>4end:0end:1end:2cost:3.00600004196
守护进程
#! /user/bin/env python#encoding=utf-8__author__ = 'chw'import threadingimport timeclass 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)> 4cost: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 threadingimport timedef run(n): global num num+=1if __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 threadingimport timedef 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 threadingimport timedef 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 threadingimport timedef 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 threadingimport timedef 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+=1def 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 Timerdef 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
- 多线程
- 多线程
- 多线程
- 多线程
- 多线程
- 多线程
- 多线程
- 多线程
- 多线程
- 多线程
- 多线程
- 多线程
- 多线程
- 多线程
- 多线程
- 多线程
- 多线程
- 多线程
- c++语言统计数字个数
- 一天搞定CSS:盒模型content、padding、border、margin--06
- swift oc混编 打包framwork
- Leetcode学习(18)—— Detect Capital
- C4.5决策树算法(Python实现)
- 多线程
- JavaScript语言基础---(十二)dom模型
- java类设计的基本原则
- Oracle11gR2 RAC for Windows安装下篇:Database 安装 简要步骤
- 设计模式->结构型模式->外观模式(门面模式)
- linux下unlink的使用
- Linux Namespace
- Deadline
- hdfs shell命令