Python 之 threading

Python线程简介: 

  Threading 用于提供线程相关的操作,线程是应用程序中工作的最小单位。

threading 模块提供的类

   Thread, Lock, Rlock, Condition, Semaphore, Event, Timer, local

threading 模块提供的常用方法

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

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

   threading.activeCount():   返回正在运行的线程数量。

threading 模块提供的常量

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

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"%arg)
 8   t_list= []
 9   for i in range(20):   
10      t = threading.Thread(target=action,args=("hello",))   #target 填写要执行的方法名称 args 给要执行的方法传入参数。
11      t_list.append(i)
12      t.start()
13  for i in t_list:
14      t.join()
15 #方法二: 从Thread 继承,并重写run()
16 class Mythread(threading.Thread):
17     def __init__(self,agr):
18         super(Mythread,self).__init__()  #注意:一定要显示的调用父类的初始化函数
19         self.agr = agr
20     def run(self):                       # 方法名称一定要定义为run Thread才会调用这个方法
21         time.sleep(1)
22         print("the arg is %s" %self.agr)
23 for i in range(20):
24     t = Mythread("test")
25     t.start()
构造方法

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

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

  target:要执行的方法;

  name:线程名;

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

实例方法

  isAlive():  返回线程是否再运行。

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

   start(): 线程准备就绪,等待CPU调度。启动线程

   s/setDaemon(bool): 获取/设置后台线程(默认前台线程(False )).

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

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

   join: 将线程变成了串行,逐个执行每个线程,执行完毕后继续往下执行。

使用例子(未设置setDaemon) 守护进程

 验证了setDaemon(False)前台线程,主线程执行过程中,前台线程也在进行,主线程执行完毕后,等待前台线程也执行完成后,程序停止

 1 #-*- coding:utf-8 -*-
 2 import threading
 3 import time
 4 def action(arg):
 5     time.sleep(1)
 6     print("sub thread start! the thread name is %s" %threading.current_thread().getName())
 7     print("the arg is %s"%arg)
 8     time.sleep(1)
 9 for i in range(4):
10     t = threading.Thread(target=action,args=(i,))
11     t.start()
12 
13 print("main thread done")
14 #执行结果
15 main thread done
16 sub thread start! the thread name is Thread-3
17 the arg is 2
18 sub thread start! the thread name is Thread-2
19 the arg is 1
20 sub thread start! the thread name is Thread-1
21 the arg is 0
22 sub thread start! the thread name is Thread-4
23 the arg is 3
24 进程已结束,退出代码0
View Code
使用例子二(setDeamon =True)

 

 1 #-*- coding:utf-8 -*-
 2   import threading
 3   import time
 4   def action(arg):
 5       time.sleep(1)
 6       print("sub thread start! the thread name is %s" %threading.current_thread().getName())
 7       print("the arg is %s"%arg)
 8       time.sleep(1)
 9   for i in range(4):
10      t = threading.Thread(target=action,args=(i,))
11      t.setDaemon(True)
12      t.start()
13  
14  print("main thread done")
15  #执行结果
16  main thread done
17  
18  进程已结束,退出代码0
View Code
使用join 例子

 

 1 #-*- coding:utf-8 -*-
 2 import threading
 3 import time
 4 def action(arg):
 5     time.sleep(1)
 6     print("sub thread start! the thread name is %s" %threading.current_thread().getName())
 7     print("the arg is %s"%arg)
 8     time.sleep(1)
 9 t_list = []
10 for i in range(4):
11     t = threading.Thread(target=action,args=(i,))
12     t.start()
13     t_list.append(t)
14 for i in t_list:
15     i.join()
16 print("main thread done")
17 #执行结果
18 sub thread start! the thread name is Thread-1
19 the arg is 0
20 sub thread start! the thread name is Thread-3
21 the arg is 2
22 sub thread start! the thread name is Thread-2
23 the arg is 1
24 sub thread start! the thread name is Thread-4
25 the arg is 3
26 main thread done
27 
28 进程已结束,退出代码0
View Code

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

JOIN 使用不妥当的示例

 

 1 #-*- coding:utf-8 -*-
 2 import threading
 3 import time
 4 def action(arg):
 5     time.sleep(1)
 6     print("sub thread start! the thread name is %s" %threading.current_thread().getName())
 7     print("the arg is %s"%arg)
 8     time.sleep(1)
 9 for i in range(4):
10     t = threading.Thread(target=action,args=(i,))
11     t.start()
12     t.join()
13 print("main thread done")
14 #执行结果
15 vsub thread start! the thread name is Thread-1
16 the arg is 0
17 sub thread start! the thread name is Thread-2
18 the arg is 1
19 sub thread start! the thread name is Thread-3
20 the arg is 2
21 sub thread start! the thread name is Thread-4
22 the arg is 3
23 main thread done
24 
25 进程已结束,退出代码0
View Code
Lock

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

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

构造方法: 
  Lock()

实例方法: 
  acquire([timeout]): 使线程进入同步阻塞状态,尝试获得锁定。 
  release(): 释放锁。使用前线程必须已获得锁定,否则将抛出异常

 1 # encoding: UTF-8
 2 import threading
 3 import time
 4  
 5 data = 0
 6 lock = threading.Lock()
 7  
 8 def func():
 9     global data
10     print '%s acquire lock...' % threading.currentThread().getName()
11     
12     # 调用acquire([timeout])时,线程将一直阻塞,
13     # 直到获得锁定或者直到timeout秒后(timeout参数可选)。
14     # 返回是否获得锁。
15     if lock.acquire():
16         print '%s get the lock.' % threading.currentThread().getName()
17         data += 1
18         time.sleep(2)
19         print '%s release lock...' % threading.currentThread().getName()
20         
21         # 调用release()将释放锁。
22         lock.release()
23  
24 t1 = threading.Thread(target=func)
25 t2 = threading.Thread(target=func)
26 t3 = threading.Thread(target=func)
27 t1.start()
28 t2.start()
29 t3.start()
View Code
Rlock

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

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

构造方法: 
  RLock()

实例方法: 
  acquire([timeout])/release(): 跟Lock差不多。

 1 #encoding: UTF-8
 2 import threading
 3 import time
 4  
 5 rlock = threading.RLock()
 6  
 7 def func():
 8     # 第一次请求锁定
 9     print '%s acquire lock...' % threading.currentThread().getName()
10     if rlock.acquire():
11         print '%s get the lock.' % threading.currentThread().getName()
12         time.sleep(2)
13         
14         # 第二次请求锁定
15         print '%s acquire lock again...' % threading.currentThread().getName()
16         if rlock.acquire():
17             print '%s get the lock.' % threading.currentThread().getName()
18             time.sleep(2)
19         
20         # 第一次释放锁
21         print '%s release lock...' % threading.currentThread().getName()
22         rlock.release()
23         time.sleep(2)
24         
25         # 第二次释放锁
26         print '%s release lock...' % threading.currentThread().getName()
27         rlock.release()
28  
29 t1 = threading.Thread(target=func)
30 t2 = threading.Thread(target=func)
31 t3 = threading.Thread(target=func)
32 t1.start()
33 t2.start()
34 t3.start()
View Code
Condition

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

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

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

实例方法: 
  acquire([timeout])/release(): 调用关联的锁的相应方法。 
  wait([timeout]): 调用这个方法将使线程进入Condition的等待池等待通知,并释放锁。使用前线程必须已获得锁定,否则将抛出异常。 
  notify(): 调用这个方法将从等待池挑选一个线程并通知,收到通知的线程将自动调用acquire()尝试获得锁定(进入锁定池);其他线程仍然在等待池中。调用这个方法不会释放锁定。使用前线程必须已获得锁定,否则将抛出异常。 
  notifyAll(): 调用这个方法将通知等待池中所有的线程,这些线程都将进入锁定池尝试获得锁定。调用这个方法不会释放锁定。使用前线程必须已获得锁定,否则将抛出异常。

示例  生产者/消费者

 

 1 # encoding: UTF-8
 2 import threading
 3 import time
 4  
 5 # 商品
 6 product = None
 7 # 条件变量
 8 con = threading.Condition()
 9  
10 # 生产者方法
11 def produce():
12     global product
13     
14     if con.acquire():
15         while True:
16             if product is None:
17                 print 'produce...'
18                 product = 'anything'
19                 
20                 # 通知消费者,商品已经生产
21                 con.notify()
22             
23             # 等待通知
24             con.wait()
25             time.sleep(2)
26  
27 # 消费者方法
28 def consume():
29     global product
30     
31     if con.acquire():
32         while True:
33             if product is not None:
34                 print 'consume...'
35                 product = None
36                 
37                 # 通知生产者,商品已经没了
38                 con.notify()
39             
40             # 等待通知
41             con.wait()
42             time.sleep(2)
43  
44 t1 = threading.Thread(target=produce)
45 t2 = threading.Thread(target=consume)
46 t2.start()
47 t1.start()
View Code
Semaphore /BoundeSemaphore

  Semaphore(信号量)是计算机科学史上最古老的同步指令之一。

  Semaphore管理一个内置的计数器,每当调用acquire()时-1,调用release() 时+1。计数器不能小于0;当计数器为0时,acquire()将阻塞线程至同步锁定状态,直到其他线程调用release()。

基于这个特点,Semaphore经常用来同步一些有“访客上限”的对象,比如连接池。

  BoundedSemaphore 与Semaphore的唯一区别在于前者将在调用release()时检查计数器的值是否超过了计数器的初始值,如果超过了将抛出一个异常。

构造方法: 
  Semaphore(value=1): value是计数器的初始值。

实例方法: 
  acquire([timeout]): 请求Semaphore。如果计数器为0,将阻塞线程至同步阻塞状态;否则将计数器-1并立即返回。 
  release(): 释放Semaphore,将计数器+1,如果使用BoundedSemaphore,还将进行释放次数检查。release()方法不检查线程是否已获得 Semaphore。

 1 # encoding: UTF-8
 2 import threading
 3 import time
 4  
 5 # 计数器初值为2
 6 semaphore = threading.Semaphore(2)
 7  
 8 def func():
 9     
10     # 请求Semaphore,成功后计数器-1;计数器为0时阻塞
11     print '%s acquire semaphore...' % threading.currentThread().getName()
12     if semaphore.acquire():
13         
14         print '%s get semaphore' % threading.currentThread().getName()
15         time.sleep(4)
16         
17         # 释放Semaphore,计数器+1
18         print '%s release semaphore' % threading.currentThread().getName()
19         semaphore.release()
20  
21 t1 = threading.Thread(target=func)
22 t2 = threading.Thread(target=func)
23 t3 = threading.Thread(target=func)
24 t4 = threading.Thread(target=func)
25 t1.start()
26 t2.start()
27 t3.start()
28 t4.start()
29  
30 time.sleep(2)
31  
32 # 没有获得semaphore的主线程也可以调用release
33 # 若使用BoundedSemaphore,t4释放semaphore时将抛出异常
34 print 'MainThread release semaphore without acquire'
35 semaphore.release()
View Code
Event

  Event(事件)是最简单的线程通信机制之一:一个线程通知事件,其他线程等待事件。Event内置了一个初始为False的标志,当调用set()时设为True,调用clear()时重置为 False。wait()将阻塞线程至等待阻塞状态。

  Event其实就是一个简化版的 Condition。Event没有锁,无法使线程进入同步阻塞状态。

构造方法: 
  Event()

实例方法: 
  isSet(): 当内置标志为True时返回True。 
  set(): 将标志设为True,并通知所有处于等待阻塞状态的线程恢复运行状态。 
  lear(): 将标志设为False。 
  wait([timeout]): 如果标志为True将立即返回,否则阻塞线程至等待阻塞状态,等待其他线程调用set()。

 1 # encoding: UTF-8
 2 import threading
 3 import time
 4  
 5 event = threading.Event()
 6  
 7 def func():
 8     # 等待事件,进入等待阻塞状态
 9     print '%s wait for event...' % threading.currentThread().getName()
10     event.wait()
11     
12     # 收到事件后进入运行状态
13     print '%s recv event.' % threading.currentThread().getName()
14  
15 t1 = threading.Thread(target=func)
16 t2 = threading.Thread(target=func)
17 t1.start()
18 t2.start()
19  
20 time.sleep(2)
21  
22 # 发送事件通知
23 print 'MainThread set event.'
24 event.set()
View Code
local

  local是一个小写字母开头的类,用于管理 thread-local(线程局部的)数据。对于同一个local,线程无法访问其他线程设置的属性;线程设置的属性不会被其他线程设置的同名属性替换。

  可以把local看成是一个“线程-属性字典”的字典,local封装了从自身使用线程作为 key检索对应的属性字典、再使用属性名作为key检索属性值的细节。

 1 # encoding: UTF-8
 2 import threading
 3  
 4 local = threading.local()
 5 local.tname = 'main'
 6  
 7 def func():
 8     local.tname = 'notmain'
 9     print local.tname
10  
11 t1 = threading.Thread(target=func)
12 t1.start()
13 t1.join()
14  
15 print local.tname
View Code

熟练掌握Thread、Lock、Condition就可以应对绝大多数需要使用线程的场合,某些情况下local也是非常有用的东西。本文的最后使用这几个类展示线程基础中提到的场景:

 1 # encoding: UTF-8
 2 import threading
 3  
 4 alist = None
 5 condition = threading.Condition()
 6  
 7 def doSet():
 8     if condition.acquire():
 9         while alist is None:
10             condition.wait()
11         for i in range(len(alist))[::-1]:
12             alist[i] = 1
13         condition.release()
14  
15 def doPrint():
16     if condition.acquire():
17         while alist is None:
18             condition.wait()
19         for i in alist:
20             print i,
21         print
22         condition.release()
23  
24 def doCreate():
25     global alist
26     if condition.acquire():
27         if alist is None:
28             alist = [0 for i in range(10)]
29             condition.notifyAll()
30         condition.release()
31  
32 tset = threading.Thread(target=doSet,name='tset')
33 tprint = threading.Thread(target=doPrint,name='tprint')
34 tcreate = threading.Thread(target=doCreate,name='tcreate')
35 tset.start()
36 tprint.start()
37 tcreate.start()
View Code
Timer

  Timer(定时器)是Thread的派生类,用于在指定时间后调用一个方法。

造方法: 
  Timer(interval, function, args=[], kwargs={}) 
  interval: 指定的时间 
  function: 要执行的方法 
  args/kwargs: 方法的参数

实例方法: 
Timer从Thread派生,没有增加实例方法。

1 # encoding: UTF-8
2 import threading
3  
4 def func():
5     print 'hello timer!'
6  
7 timer = threading.Timer(5, func)
8 timer.start()
View Code

 

posted @ 2017-09-04 12:37  lichenxi  阅读(200)  评论(0编辑  收藏  举报