python--threading多线程总结

本文详细介绍了Python的threading模块,包括Thread、Lock、Rlock、Condition、Event、timer和local类的使用方法和实例,通过示例展示了它们在多线程中的作用,如线程同步、事件通信和定时任务。同时,强调了Lock和Rlock的区别,并提供了生产者消费者模型的应用案例。
摘要由CSDN通过智能技术生成

threading用于提供线程相关的操作,线程是应用程序中工作的最小单元。python当前版本的多线程库没有实现优先级、线程组,线程也不能被停止、暂停、恢复、中断。

  如果想了解更多Python的伙伴或者小白中有任何困难不懂的可以加入我的python交流学习QQ群:973998104,多多交流问题,互帮互助,群里有不错的学习教程和开发工具。资源分享

threading模块提供的类:

 
  1. Thread, Lock, Rlock, Condition, [Bounded]Semaphore, Event, Timer, local。

threading 模块提供的常用方法:

 
  1. threading.currentThread(): 返回当前的线程变量。

  2. threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。

  3. threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。

threading 模块提供的常量:

 
  1. threading.TIMEOUT_MAX 设置threading全局超时时间。

Thread类

Thread是线程类,有两种使用方法,直接传入要运行的方法或从Thread继承并覆盖run():

 
  1. # coding:utf-8

  2. import threading

  3. import time

  4. #方法一:将要执行的方法作为参数传给Thread的构造方法

  5. def action(arg):

  6.    time.sleep(1)

  7.    print 'the arg is:%s\r' %arg

  8.  

  9. for i in xrange(4):

  10.    t =threading.Thread(target=action,args=(i,))

  11.    t.start()

  12.  

  13. print 'main thread end!'

  14.  

  15. #方法二:从Thread继承,并重写run()

  16. class MyThread(threading.Thread):

  17.    def __init__(self,arg):

  18.        super(MyThread, self).__init__()#注意:一定要显式的调用父类的初始化函数。

  19.        self.arg=arg

  20.    def run(self):#定义每个线程要运行的函数

  21.        time.sleep(1)

  22.        print 'the arg is:%s\r' % self.arg

  23.  

  24. for i in xrange(4):

  25.    t =MyThread(i)

  26.    t.start()

  27.  

  28. print 'main thread end!'

构造方法:

 
  1. Thread(group=None, target=None, name=None, args=(), kwargs={})

  2.  

  3.   group: 线程组,目前还没有实现,库引用中提示必须是None;

  4.   target: 要执行的方法;

  5.   name: 线程名;

  6.   args/kwargs: 要传入方法的参数。

实例方法:

 
  1. isAlive(): 返回线程是否在运行。正在运行指启动后、终止前。

  2. get/setName(name): 获取/设置线程名。

  3. start():  线程准备就绪,等待CPU调度

  4. is/setDaemon(bool): 获取/设置是后台线程(默认前台线程(False))。(在start之前设置)

  5.    如果是后台线程,主线程执行过程中,后台线程也在进行,主线程执行完毕后,后台线程不论成功与否,主线程和后台线程均停止

  6.    如果是前台线程,主线程执行过程中,前台线程也在进行,主线程执行完毕后,等待前台线程也执行完成后,程序停止

  7. start(): 启动线程。

  8. join([timeout]): 阻塞当前上下文环境的线程,直到调用此方法的线程终止或到达指定的timeout(可选参数)。

使用例子一(未设置setDeamon):

 
  1. # coding:utf-8

  2. import threading

  3. import time

  4.  

  5. def action(arg):

  6.    time.sleep(1)

  7.    print  'sub thread start!the thread name is:%s\r' % threading.currentThread().getName()

  8.    print 'the arg is:%s\r' %arg

  9.    time.sleep(1)

  10.  

  11. for i in xrange(4):

  12.    t =threading.Thread(target=action,args=(i,))

  13.    t.start()

  14.  

  15. print 'main_thread end!'

运行结果

 
  1. main_thread end!

  2. sub thread start!the thread name is:Thread-2

  3. the arg is:1

  4. the arg is:0

  5. sub thread start!the thread name is:Thread-4

  6. the arg is:2

  7. the arg is:3

  8. Process finished with exit code 0

可以看出,创建的4个“前台”线程,主线程执行过程中,前台线程也在进行,主线程执行完毕后,等待前台线程也执行完成后,程序停止 复制代码 验证了serDeamon(False)(默认)前台线程,主线程执行过程中,前台线程也在进行,主线程执行完毕后,等待前台线程也执行完成后,主线程停止。

使用例子二(setDeamon=True)

 
  1. # coding:utf-8

  2. import threading

  3. import time

  4.  

  5. def action(arg):

  6.    time.sleep(1)

  7.    print  'sub thread start!the thread name is:%s\r' % threading.currentThread().getName()

  8.    print 'the arg is:%s\r' %arg

  9.    time.sleep(1)

  10.  

  11. for i in xrange(4):

  12.    t =threading.Thread(target=action,args=(i,))

  13.    t.setDaemon(True)#设置线程为后台线程

  14.    t.start()

  15.  

  16. print 'main_thread end!'

运行结果

 
  1. main_thread end!

  2. Process finished with exit code 0

  3. 可以看出,主线程执行完毕后,后台线程不管是成功与否,主线程均停止

验证了serDeamon(True)后台线程,主线程执行过程中,后台线程也在进行,主线程执行完毕后,后台线程不论成功与否,主线程均停止。

使用例子三(设置join)

 
  1. #coding:utf-8

  2. import threading

  3. import time

  4.  

  5. def action(arg):

  6.    time.sleep(1)

  7.    print  'sub thread start!the thread name is:%s    ' % threading.currentThread().getName()

  8.    print 'the arg is:%s   ' %arg

  9.    time.sleep(1)

  10.  

  11. thread_list = []    #线程存放列表

  12. for i in xrange(4):

  13.    t =threading.Thread(target=action,args=(i,))

  14.    t.setDaemon(True)

  15.    thread_list.append(t)

  16.  

  17. for t in thread_list:

  18.    t.start()

  19.  

  20. for t in thread_list:

  21.    t.join()

运行结果

 
  1. sub thread start!the thread name is:Thread-2    

  2. the arg is:1  

  3. sub thread start!the thread name is:Thread-3    

  4. the arg is:2  

  5. sub thread start!the thread name is:Thread-1    

  6. the arg is:0  

  7. sub thread start!the thread name is:Thread-4    

  8. the arg is:3  

  9. main_thread end!

  10.  

  11. Process finished with exit code 0

  12. 设置join之后,主线程等待子线程全部执行完成后或者子线程超时后,主线程才结束

验证了 join()阻塞当前上下文环境的线程,直到调用此方法的线程终止或到达指定的timeout,即使设置了setDeamon(True)主线程依然要等待子线程结束。

使用例子四(join不妥当的用法,使多线程编程顺序执行)

 
  1. #coding:utf-8

  2. import threading

  3. import time

  4.  

  5. def action(arg):

  6.    time.sleep(1)

  7.    print  'sub thread start!the thread name is:%s    ' % threading.currentThread().getName()

  8.    print 'the arg is:%s   ' %arg

  9.    time.sleep(1)

  10.  

  11.  

  12. for i in xrange(4):

  13.    t =threading.Thread(target=action,args=(i,))

  14.    t.setDaemon(True)

  15.    t.start()

  16.    t.join()

  17.  

  18. print 'main_thread end!'

运行结果

 
  1. sub thread start!the thread name is:Thread-1    

  2. the arg is:0  

  3. sub thread start!the thread name is:Thread-2    

  4. the arg is:1  

  5. sub thread start!the thread name is:Thread-3    

  6. the arg is:2  

  7. sub thread start!the thread name is:Thread-4    

  8. the arg is:3  

  9. main_thread end!

  10.  

  11. Process finished with exit code 0

  12. 可以看出此时,程序只能顺序执行,每个线程都被上一个线程的join阻塞,使得“多线程”失去了多线程意义。

Lock、Rlock类

  由于线程之间随机调度:某线程可能在执行n条后,CPU接着执行其他线程。为了多个线程同时操作一个内存中的资源时不产生混乱,我们使用锁。

Lock(指令锁)是可用的最低级的同步指令。Lock处于锁定状态时,不被特定的线程拥有。Lock包含两种状态——锁定和非锁定,以及两个基本的方法。

可以认为Lock有一个锁定池,当线程请求锁定时,将线程至于池中,直到获得锁定后出池。池中的线程处于状态图中的同步阻塞状态。

RLock(可重入锁)是一个可以被同一个线程请求多次的同步指令。RLock使用了“拥有的线程”和“递归等级”的概念,处于锁定状态时,RLock被某个线程拥有。拥有RLock的线程可以再次调用acquire(),释放锁时需要调用release()相同次数。

可以认为RLock包含一个锁定池和一个初始值为0的计数器,每次成功调用 acquire()/release(),计数器将+1/-1,为0时锁处于未锁定状态。

简言之:Lock属于全局,Rlock属于线程。

构造方法: Lock(),Rlock(),推荐使用Rlock()

实例方法:

 
  1.   acquire([timeout]): 尝试获得锁定。使线程进入同步阻塞状态。

  2.   release(): 释放锁。使用前线程必须已获得锁定,否则将抛出异常。

例子一(未使用锁):

 
  1. #coding:utf-8

  2. import threading

  3. import time

  4.  

  5. gl_num = 0

  6.  

  7. def show(arg):

  8.    global gl_num

  9.    time.sleep(1)

  10.    gl_num +=1

  11.    print gl_num

  12.  

  13. for i in range(10):

  14.    t = threading.Thread(target=show, args=(i,))

  15.    t.start()

  16.  

  17. print 'main thread stop'

运行结果

 
  1. main thread stop

  2. 12

  3.  

  4. 3

  5. 4

  6. 568

  7. 9

  8.  

  9. 910

  10.  

  11.  

  12. Process finished with exit code 0

  13.  

  14. 多次运行可能产生混乱。这种场景就是适合使用锁的场景。

例子二(使用锁):

 
  1. # coding:utf-8

  2.  

  3. import threading

  4. import time

  5.  

  6. gl_num = 0

  7.  

  8. lock = threading.RLock()

  9.  

  10.  

  11. # 调用acquire([timeout])时,线程将一直阻塞,

  12. # 直到获得锁定或者直到timeout秒后(timeout参数可选)。

  13. # 返回是否获得锁。

  14. def Func():

  15.    lock.acquire()

  16.    global gl_num

  17.    gl_num += 1

  18.    time.sleep(1)

  19.    print gl_num

  20.    lock.release()

  21.  

  22.  

  23. for i in range(10):

  24.    t = threading.Thread(target=Func)

  25.    t.start()

运行结果

 
  1. 1

  2. 2

  3. 3

  4. 4

  5. 5

  6. 6

  7. 7

  8. 8

  9. 9

  10. 10

  11.  

  12. Process finished with exit code 0

  13. 可以看出,全局变量在在每次被调用时都要获得锁,才能操作,因此保证了共享数据的安全性

Lock对比Rlock

 
  1. #coding:utf-8

  2.  

  3. import threading

  4. lock = threading.Lock() #Lock对象

  5. lock.acquire()

  6. lock.acquire()  #产生了死锁。

  7. lock.release()

  8. lock.release()

  9. print lock.acquire()

  10.  

  11.  

  12. import threading

  13. rLock = threading.RLock()  #RLock对象

  14. rLock.acquire()

  15. rLock.acquire() #在同一线程内,程序不会堵塞。

  16. rLock.release()

  17. rLock.release()

Condition类

  Condition(条件变量)通常与一个锁关联。需要在多个Contidion中共享一个锁时,可以传递一个Lock/RLock实例给构造方法,否则它将自己生成一个RLock实例。

  可以认为,除了Lock带有的锁定池外,Condition还包含一个等待池,池中的线程处于等待阻塞状态,直到另一个线程调用notify()/notifyAll()通知;得到通知后线程进入锁定池等待锁定。

构造方法: Condition([lock/rlock])

实例方法:

 
  1. acquire([timeout])/release(): 调用关联的锁的相应方法。

  2. wait([timeout]): 调用这个方法将使线程进入Condition的等待池等待通知,并释放锁。使用前线程必须已获得锁定,否则将抛出异常。

  3. notify(): 调用这个方法将从等待池挑选一个线程并通知,收到通知的线程将自动调用acquire()尝试获得锁定(进入锁定池);其他线程仍然在等待池中。调用这个方法不会释放锁定。使用前线程必须已获得锁定,否则将抛出异常。

  4. notifyAll(): 调用这个方法将通知等待池中所有的线程,这些线程都将进入锁定池尝试获得锁定。调用这个方法不会释放锁定。使用前线程必须已获得锁定,否则将抛出异常。

例子一:生产者消费者模型

 
  1. # encoding: UTF-8

  2. import threading

  3. import time

  4.  

  5. # 商品

  6. product = None

  7. # 条件变量

  8. con = threading.Condition()

  9.  

  10.  

  11. # 生产者方法

  12. def produce():

  13.    global product

  14.  

  15.    if con.acquire():

  16.        while True:

  17.            if product is None:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值