Python3入门之线程threading常用方法

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

Python3⼊门之线程threading常⽤⽅法
Python3 线程中常⽤的两个模块为:
_thread
threading(推荐使⽤)
thread 模块已被废弃。

⽤户可以使⽤ threading 模块代替。

所以,在 Python3 中不能再使⽤"thread" 模块。

为了兼容性,Python3 将 thread 重命名为
"_thread"。

下⾯将介绍threading模块常⽤⽅法:
1. threading.Lock()
如果多个线程共同对某个数据修改,则可能出现不可预料的结果,为了保证数据的正确性,需要对多个线程进⾏同步。

使⽤ Thread 对象的 Lock 和 Rlock 可以实现简单的线程同步,这两个对象都有 acquire ⽅法和 release ⽅法,对于那些需要每次只允许⼀个线程操作的数据,可以将其操作放到 acquire 和 release ⽅法之间。

来看看多个线程同时操作⼀个变量怎么把内容给改乱了(在windows下不会出现内容混乱情况,可能python在Windows下⾃动加上锁了;不过在Linux 下可以测试出内容会被改乱):
#!/usr/bin/env python3
import time, threading
# 假定这是你的银⾏存款:
balance = 0
def change_it(n):
# 先存后取,结果应该为0:
global balance
balance = balance + n
balance = balance - n
def run_thread(n):
for i in range(100000):
change_it(n)
t1 = threading.Thread(target=run_thread, args=(5,))
t2 = threading.Thread(target=run_thread, args=(8,))
t1.start()
t2.start()
t1.join()
t2.join()
print(balance)
执⾏结果:
[root@localhost ~]# python3 thread_lock.py
5
[root@localhost ~]# python3 thread_lock.py
5
[root@localhost ~]# python3 thread_lock.py
[root@localhost ~]# python3 thread_lock.py
8
[root@localhost ~]# python3 thread_lock.py
-8
[root@localhost ~]# python3 thread_lock.py
5
[root@localhost ~]# python3 thread_lock.py
-8
[root@localhost ~]# python3 thread_lock.py
3
[root@localhost ~]# python3 thread_lock.py
5
我们定义了⼀个共享变量balance,初始值为0,并且启动两个线程,先存后取,理论上结果应该为0,但是,由于线程的调度是由操作系统决定的,当t1、t2交替执⾏时,只要循环次数⾜够多,balance的结果就不⼀定是0了。

如果我们要确保balance计算正确,就要给change_it()上⼀把锁,当某个线程开始执⾏change_it()时,我们说,该线程因为获得了锁,因此其他线程不能同时执⾏change_it(),只能等待,直到锁被释放后,获得该锁以后才能改。

由于锁只有⼀个,⽆论多少线程,同⼀时刻最多只有⼀个线程持有该锁,所以,不会造成修改的冲突。

创建⼀个锁就是通过threading.Lock()来实现:
#!/usr/bin/env python3
import time, threading
balance = 0
lock = threading.Lock()
def change_it(n):
# 先存后取,结果应该为0:
global balance
balance = balance + n
balance = balance - n
def run_thread(n):
for i in range(100000):
# 先要获取锁:
lock.acquire()
try:
# 放⼼地改吧:
change_it(n)
finally:
# 改完了⼀定要释放锁:
lock.release()
t1 = threading.Thread(target=run_thread, args=(5,))
t2 = threading.Thread(target=run_thread, args=(8,))
t1.start()
t2.start()
t1.join()
t2.join()
print(balance)
执⾏结果:
[root@localhost ~]# python3 thread_lock.py
[root@localhost ~]# python3 thread_lock.py
[root@localhost ~]# python3 thread_lock.py
[root@localhost ~]# python3 thread_lock.py
[root@localhost ~]# python3 thread_lock.py
[root@localhost ~]# python3 thread_lock.py
当多个线程同时执⾏lock.acquire()时,只有⼀个线程能成功地获取锁,然后继续执⾏代码,其他线程就继续等待直到获得锁为⽌。

获得锁的线程⽤完后⼀定要释放锁,否则那些苦苦等待锁的线程将永远等待下去,成为死线程。

所以我们⽤try...finally来确保锁⼀定会被释放。

锁的好处就是确保了某段关键代码只能由⼀个线程从头到尾完整地执⾏,坏处当然也很多,⾸先是阻⽌了多线程并发执⾏,包含锁的某段代码实际上只能以单线程模式执⾏,效率就⼤⼤地下降了。

其次,由于可以存在多个锁,不同的线程持有不同的锁,并试图获取对⽅持有的锁时,可能会造成死锁,导致多个线程全部挂起,既不能执⾏,也⽆法结束,只能靠操作系统强制终⽌。

2. threading.Rlock()
RLock允许在同⼀线程中被多次acquire。

⽽Lock却不允许这种情况。

注意:如果使⽤RLock,那么acquire和release必须成对出现,即调⽤了n次acquire,必须调⽤n次的release才能真正释放所占⽤的琐。

import threading
lock = threading.Lock()
#Lock对象
lock.acquire()
lock.acquire()
#产⽣了死琐。

lock.release()
lock.release()
import threading
rLock = threading.RLock()
#RLock对象
rLock.acquire()
rLock.acquire()
#在同⼀线程内,程序不会堵塞。

rLock.release()
rLock.release()
3. threading.Condition()
可以把Condiftion理解为⼀把⾼级的琐,它提供了⽐Lock, RLock更⾼级的功能,允许我们能够控制复杂的线程同步问题。

threadiong.Condition在内部维护⼀个琐对象(默认是RLock),可以在创建Condigtion对象的时候把琐对象作为参数传⼊。

Condition也提供了acquire, release⽅法,其含义与琐的acquire, release⽅法⼀致,其实它只是简单的调⽤内部琐对象的对应的⽅法⽽已。

Condition还提供wait⽅法、notify⽅法、notifyAll⽅法(特别要注意:这些⽅法只有在占⽤琐(acquire)之后才能调⽤,否则将会报RuntimeError异常。

):
acquire()/release():获得/释放 Lock
wait([timeout]):线程挂起,直到收到⼀个notify通知或者超时(可选的,浮点数,单位是秒s)才会被唤醒继续运⾏。

wait()必须在已获得Lock前提下才能调⽤,否则会触发RuntimeError。

调⽤wait()会释放Lock,直⾄该线程被Notify()、NotifyAll()或者超时线程⼜重新获得Lock.
notify(n=1):通知其他线程,那些挂起的线程接到这个通知之后会开始运⾏,默认是通知⼀个正等待该condition的线程,最多则唤醒n个等待的线程。

notify()必须在已获得Lock前提下才能调⽤,否则会触发RuntimeError。

notify()不会主动释放Lock。

notifyAll(): 如果wait状态线程⽐较多,notifyAll的作⽤就是通知所有线程(这个⼀般⽤得少)
现在写个捉迷藏的游戏来具体介绍threading.Condition的基本使⽤。

假设这个游戏由两个⼈来玩,⼀个藏(Hider),⼀个找(Seeker)。

游戏的规则如下:1.游戏开始之后,Seeker先把⾃⼰眼睛蒙上,蒙上眼睛后,就通知Hider;2. Hider接收通知后开始找地⽅将⾃⼰藏起来,藏好之后,再通知Seeker可以找了;
3. Seeker接收到通知之后,就开始找Hider。

Hider和Seeker都是独⽴的个体,在程序中⽤两个独⽴的线程来表⽰,在游戏过程中,两者之间的⾏为有⼀定的时序关系,我们通过Condition来控制这种时序关系。

#!/usr/bin/python3.4
# -*- coding: utf-8 -*-
import threading, time
def Seeker(cond, name):
time.sleep(2)
cond.acquire()
print('%s :我已经把眼睛蒙上了!'% name)
cond.notify()
cond.wait()
for i in range(3):
print('%s is finding!!!'% name)
time.sleep(2)
cond.notify()
cond.release()
print('%s :我赢了!'% name)
def Hider(cond, name):
cond.acquire()
cond.wait()
for i in range(2):
print('%s is hiding!!!'% name)
time.sleep(3)
print('%s :我已经藏好了,你快来找我吧!'% name)
cond.notify()
cond.wait()
cond.release()
print('%s :被你找到了,唉~^~!'% name)
if__name__ == '__main__':
cond = threading.Condition()
seeker = threading.Thread(target=Seeker, args=(cond, 'seeker'))
hider = threading.Thread(target=Hider, args=(cond, 'hider'))
seeker.start()
hider.start()
执⾏结果:
seeker :我已经把眼睛蒙上了!
hider is hiding!!!
hider is hiding!!!
hider :我已经藏好了,你快来找我吧!
seeker is finding!!!
seeker is finding!!!
seeker is finding!!!
seeker :我赢了!
hider :被你找到了,唉~^~!
上⾯不同颜⾊的notify和wait⼀⼀对应关系,通知--->等待;等待--->通知
4. threading.Semaphore和BoundedSemaphore
Semaphore:Semaphore 在内部管理着⼀个计数器。

调⽤ acquire() 会使这个计数器 -1,release() 则是+1(可以多次release(),所以计数器的值理论上可以⽆限).计数器的值永远不会⼩于 0,当计数器到 0 时,再调⽤ acquire() 就会阻塞,直到其他线程来调⽤release()
import threading, time
def run(n):
# 获得信号量,信号量减⼀
semaphore.acquire()
time.sleep(1)
print("run the thread: %s" % n)
# 释放信号量,信号量加⼀
semaphore.release()
#semaphore.release() # 可以多次释放信号量,每次释放计数器+1
#semaphore.release() # 可以多次释放信号量,每次释放计数器+1
if__name__ == '__main__':
num = 0
semaphore = threading.Semaphore(2) # 最多允许2个线程同时运⾏(即计数器值);在多次释放信号量后,计数器值增加后每次可以运⾏的线程数也会增加
for i in range(20):
t = threading.Thread(target=run, args=(i,))
t.start()
while threading.active_count() != 1:
pass# print threading.active_count()
else:
print('----all threads done---')
print(num)
BoundedSemaphore:类似于Semaphore;不同在于BoundedSemaphore 会检查内部计数器的值,并保证它不会⼤于初始值,如果超了,就引发⼀个ValueError。

多数情况下,semaphore ⽤于守护限制访问(但不限于 1)的资源,如果 semaphore 被 release() 过多次,这意味着存在 bug
import threading, time
def run(n):
semaphore.acquire()
time.sleep(1)
print("run the thread: %s" % n)
semaphore.release()
# 如果再次释放信号量,信号量加⼀,这是超过限定的信号量数⽬,这时会报错ValueError: Semaphore released too many times
#semaphore.release()
if__name__ == '__main__':
num = 0
semaphore = threading.BoundedSemaphore(2) # 最多允许2个线程同时运⾏
for i in range(20):
t = threading.Thread(target=run, args=(i,))
t.start()
while threading.active_count() != 1:
pass# print threading.active_count()
else:
print('----all threads done---')
print(num)
5. threading.Event
事件处理的机制:全局定义了⼀个“Flag”,如果“Flag”值为 False,那么当程序执⾏ event.wait ⽅法时就会阻塞;如果“Flag”值为True,那么执⾏event.wait ⽅法时便不再阻塞。

clear:将“Flag”设置为False
set:将“Flag”设置为True
⽤ threading.Event 实现线程间通信,使⽤threading.Event可以使⼀个线程等待其他线程的通知,我们把这个Event传递到线程对象中,
Event默认内置了⼀个标志,初始值为False。

⼀旦该线程通过wait()⽅法进⼊等待状态,直到另⼀个线程调⽤该Event的set()⽅法将内置标志设置为True 时,该Event会通知所有等待状态的线程恢复运⾏。

通过Event来实现两个或多个线程间的交互,下⾯是⼀个红绿灯的例⼦,即起动⼀个线程做交通指挥灯,⽣成⼏个线程做车辆,车辆⾏驶按红灯停,绿灯⾏的规则。

import threading, time
import random
def light():
if not event.isSet(): #初始化evet的flag为真
event.set() #wait就不阻塞 #绿灯状态
count = 0
while True:
if count < 10:
print('\033[42;1m---green light on---\033[0m')
elif count < 13:
print('\033[43;1m---yellow light on---\033[0m')
elif count < 20:
if event.isSet():
event.clear()
print('\033[41;1m---red light on---\033[0m')
else:
count = 0
event.set() #打开绿灯
time.sleep(1)
count += 1
def car(n):
while 1:
time.sleep(random.randrange(3, 10))
#print(event.isSet())
if event.isSet():
print("car [%s] is running..." % n)
else:
print('car [%s] is waiting for the red light...' % n)
event.wait() #红灯状态下调⽤wait⽅法阻塞,汽车等待状态
if__name__ == '__main__':
car_list = ['BMW', 'AUDI', 'SANTANA']
event = threading.Event()
Light = threading.Thread(target=light)
Light.start()
for i in car_list:
t = threading.Thread(target=car, args=(i,))
t.start()
6. threading.active_count()
返回当前存活的线程对象的数量;通过计算len(threading.enumerate())长度⽽来
The returned count is equal to the length of the list returned by enumerate().
import threading, time
def run():
thread = threading.current_thread()
print('%s is running...'% thread.getName()) #返回线程名称
time.sleep(10) #休眠10S⽅便统计存活线程数量
if__name__ == '__main__':
#print('The current number of threads is: %s' % threading.active_count())
for i in range(10):
print('The current number of threads is: %s' % threading.active_count()) #返回当前存活线程数量
thread_alive = threading.Thread(target=run, name='Thread-***%s***' % i)
thread_alive.start()
thread_alive.join()
print('\n%s thread is done...'% threading.current_thread().getName())
7. threading.current_thread()
Return the current Thread object, corresponding to the caller's thread of control.
返回当前线程对象
>>> threading.current_thread
<function current_thread at 0x00000000029F6C80>
>>> threading.current_thread()
<_MainThread(MainThread, started 4912)>
>>> type(threading.current_thread())
<class'threading._MainThread'>
继承线程threading⽅法;通过help(threading.current_thread())查看。

import threading, time
def run(n):
thread = threading.current_thread()
thread.setName('Thread-***%s***' % n) #⾃定义线程名称
print('-'*30)
print("Pid is :%s" % thread.ident) # 返回线程pid
#print('ThreadName is :%s' % ) # 返回线程名称
print('ThreadName is :%s'% thread.getName()) #返回线程名称
time.sleep(2)
if__name__ == '__main__':
#print('The current number of threads is: %s' % threading.active_count())
for i in range(3):
#print('The current number of threads is: %s' % threading.active_count()) #返回当前存活线程数量
thread_alive = threading.Thread(target=run, args=(i,))
thread_alive.start()
thread_alive.join()
print('\n%s thread is done...'% threading.current_thread().getName())
执⾏结果:
Pid is :11792
ThreadName is :Thread-***0***
------------------------------
Pid is :12124
ThreadName is :Thread-***1***
------------------------------
Pid is :11060
ThreadName is :Thread-***2***
MainThread thread is done...
8. threading.enumerate()
Return a list of all Thread objects currently alive
返回当前存在的所有线程对象的列表
import threading, time
def run(n):
thread = threading.current_thread()
thread.setName('Thread-***%s***' % n)
print('-'*30)
print("Pid is :%s" % thread.ident) # 返回线程pid
#print('ThreadName is :%s' % ) # 返回线程名称
print('ThreadName is :%s'% threading.enumerate()) #返回所有线程对象列表
time.sleep(2)
if__name__ == '__main__':
#print('The current number of threads is: %s' % threading.active_count())
threading.main_thread().setName('Chengd---python')
for i in range(3):
#print('The current number of threads is: %s' % threading.active_count()) #返回当前存活线程数量
thread_alive = threading.Thread(target=run, args=(i,))
thread_alive.start()
thread_alive.join()
print('\n%s thread is done...'% threading.current_thread().getName())
执⾏结果:
Pid is :12096
ThreadName is :[<_MainThread(Chengd---python, started 12228)>, <Thread(Thread-***0***, started 12096)>, <Thread(Thread-2, initial)>]
------------------------------
Pid is :10328
ThreadName is :[<_MainThread(Chengd---python, started 12228)>, <Thread(Thread-***0***, started 12096)>, <Thread(Thread-***1***, started 10328)>, <Thread(Thread-3, initial)>] ------------------------------
Pid is :6032
ThreadName is :[<_MainThread(Chengd---python, started 12228)>, <Thread(Thread-***0***, started 12096)>, <Thread(Thread-***1***, started 10328)>, <Thread(Thread-***2***, started 6032)>] Chengd---python thread is done...
9.threading.get_ident()
返回线程pid
import threading, time
def run(n):
print('-'*30)
print("Pid is :%s" % threading.get_ident()) # 返回线程pid
if__name__ == '__main__':
threading.main_thread().setName('Chengd---python') #⾃定义线程名
for i in range(3):
thread_alive = threading.Thread(target=run, args=(i,))
thread_alive.start()
thread_alive.join()
print('\n%s thread is done...'% threading.current_thread().getName()) #获取线程名
10. threading.main_thread()
返回主线程对象,类似 threading.current_thread();只不过⼀个是返回当前线程对象,⼀个是返回主线程对象
import threading, time
def run(n):
print('-'*30)
print("Now Pid is :%s" % threading.current_thread().ident) # 返回当前线程pid
print("Main Pid is :%s" % threading.main_thread().ident) # 返回主线程pid
print('Now thread is %s...' % threading.current_thread().getName()) # 获取当前线程名
print('Main thread is %s...' % threading.main_thread().getName()) # 获取主线程线程名
if__name__ == '__main__':
threading.main_thread().setName('Chengd---python') #⾃定义线程名
for i in range(3):
thread_alive = threading.Thread(target=run, args=(i,))
thread_alive.start()
time.sleep(2)
thread_alive.join()
执⾏结果:
------------------------------
Now Pid is :8984
Main Pid is :3992
Now thread is Thread-1...
Main thread is Chengd---python...
------------------------------
Now Pid is :4828
Main Pid is :3992
Now thread is Thread-2...
Main thread is Chengd---python...
------------------------------
Now Pid is :12080
Main Pid is :3992
Now thread is Thread-3...
Main thread is Chengd---python...
***********************************************************
学习永远不晚。

——⾼尔基
***********************************************************。

相关文档
最新文档