多线程

来源:互联网 发布:网络推广工作安排 编辑:程序博客网 时间: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

0 0
原创粉丝点击