相关概念:
- 一个进程内,可以有多个线程。
- 线程内分为主线程和多个子线程。
- 多个子线程之间可以共享内存和数据。
- 由于GIL(全局解释锁)的限制,threading只适合在I/O密集型程序上使用。
threading中定义的方法
threading.active_count()
返回Thread当前活动的线程数量。
threading.current_thread()
返回当前的线程。
threading.get_ident()
返回当前线程的“标识符”,该“标识符”是一个非零整数。
threading.enumerate()
返回一个列表,列表中包括有:当前活动的所有线程以及主线程。
threading.main_thread()
返回主线程对象,通常情况下,主线程就是启动Python解释器的线程。
threading中定义的类
threading.Thread(group = None, target = None, name = None, args = (), kwargs = {})
group:是为将来扩展ThreadGroup类是保留的,不需要管它。
target:一个可调用对象,也就是设置为子线程的函数。
name:为线程设置一个名称,默认为“Thread-N”的形式,N为一个整数。
args:被target调用的对象的参数,应该传入一个元组。
kwaegs:被target调用的对象的关键字参数,应该传入一个字典。
threading.Thread()类中的方法分别有
start():多线程必须显式地调用start()方法才能开始线程的活动。而且每个线程对象最多只能调用一次。
run():一般用于在子类中重写该方法,run()方法可以理解为threading.Thread()中的target参数。
join(timeout = None):阻塞主线程,等待子线程终止。
setDarmon(True):将子线程设置为守护线程(后台程序),设置为守护线程之后,主线程不会等待子线程完全执行完成,主线程执行结束就会直接退出Python程序。
isDaemon():判断子线程是否为守护线程。
is_alive():判断线程是否存活。
setName():给子线程设置一个名字。
getName():返回子线程的名字。
示例1:
import threading
import time
def cooking(n):
print('I am cooking')
time.sleep(n)
print('cooking ok')
def listening(n):
print('I am listening music')
time.sleep(n)
print('listening ok')
t1 = threading.Thread(target = cooking, args = (5,))
t2 = threading.Thread(target = listening, args = (3,))
print('Now')
t1.start()
t2.start()
print('!!!!!!')
输出如下:
Now
I am cooking
I am listening music
!!!!!!
listening ok
cooking ok
[Finished in 5.4s]
可以看出,主线程会按顺序执行,并且会等待子线程执行结束之后,才会退出程序。
示例2:
import threading
import time
def cooking(n):
print('I am cooking')
time.sleep(n)
print('cooking ok')
def listening(n):
print('I am listening music')
time.sleep(n)
print('listening ok')
t1 = threading.Thread(target = cooking, args = (5,))
t2 = threading.Thread(target = listening, args = (3,))
print('Now')
t1.setDaemon(True)
t2.setDaemon(True)
t1.start()
t2.start()
print('!!!!!!')
输出如下:
Now
I am cooking
I am listening music
!!!!!!
[Finished in 0.2s]
当把子线程设置为守护线程(设置为后台程序)的时候,主线程不会等待子线程执行完成,主线程的代码执行完成之后,就会直接退出程序。
示例3:
import threading
import time
def cooking(n):
print('I am cooking')
time.sleep(n)
print('cooking ok')
def listening(n):
print('I am listening music')
time.sleep(n)
print('listening ok')
t1 = threading.Thread(target = cooking, args = (5,))
t2 = threading.Thread(target = listening, args = (3,))
print('Now')
t1.setDaemon(True)
t2.setDaemon(True)
t1.start()
t2.start()
t1.join()
t2.join()
print('!!!!!!')
输出如下:
Now
I am cooking
I am listening music
listening ok
cooking ok
!!!!!!
[Finished in 5.2s]
这次为两个子线程都添加了join()方法,join()方法会阻塞主线程(’!!!!!!'在最后一行输出),即使两个子线程被设置为守护线程,也会等待守护线程(后台程序)执行结束之后,才会继续往下执行主线程。
可以为join()方法添加一个超时时间,如timeout = 4,则’cooking ok‘这一行,不会被输出。
示例4:
import threading
import time
class Cooking(threading.Thread):
def __init__(self, n):
super(Cooking, self).__init__()
self.n = n
def run(self):
print('I am cooking')
time.sleep(self.n)
print('cooking ok')
class Listening(threading.Thread):
def __init__(self, n):
super(Listening, self).__init__()
self.n = n
def run(self):
print('I am listening music')
time.sleep(self.n)
print('listening ok')
if __name__ == '__main__':
print('Now')
t1 = Cooking(5)
t2 = Listening(3)
t1.start()
t2.start()
print('!!!!!!')
另一种创建子线程的方式是通过继承threading.Thread,调用超类的构造方法,并且重写run()方法。
threading.Lock()
锁不属于特定的线程,锁只有“锁定"和”解锁“两种状态。它是在”解锁“的状态下创建的。
threading.Lock()的两个基本方法分别为:
acquire():尝试获取锁
release():释放锁
示例1:未加锁的情况
import threading
import time
def cooking(n):
print('My name is Alfred,')
time.sleep(1)
print('I am cooking')
time.sleep(n)
print('cooking ok')
def listening(n):
print('My name is Rookie,')
time.sleep(1)
print('I am listening music')
time.sleep(n)
print('listening ok')
t1 = threading.Thread(target = cooking, args = (5,))
t2 = threading.Thread(target = listening, args = (3,))
print('Now')
t1.start()
t2.start()
t1.join()
t2.join()
print('!!!!!!')
输出如下:
Now
My name is Alfred,
My name is Rookie,
I am listening music
I am cooking
listening ok
cooking ok
!!!!!!
[Finished in 6.2s]
假如我想输出的顺序为,先介绍一个人名“My name is Alfred.",然后说明在干什么"I am cooking’,再然后介绍下一个人名。
我具体想要达到的输出顺序,如下:
Now
My name is Alfred,
I am cooking
My name is Rookie,
I am listening music
listening ok
cooking ok
!!!!!!
示例2:加锁的情况
import threading
import time
lock = threading.Lock()
def cooking(n):
lock.acquire()
print('My name is Alfred,')
time.sleep(1)
print('I am cooking')
lock.release()
time.sleep(n)
print('cooking ok')
def listening(n):
lock.acquire()
print('My name is Rookie,')
time.sleep(1)
print('I am listening music')
lock.release()
time.sleep(n)
print('listening ok')
t1 = threading.Thread(target = cooking, args = (5,))
t2 = threading.Thread(target = listening, args = (3,))
print('Now')
t1.start()
t2.start()
t1.join()
t2.join()
print('!!!!!!')
threading.RLock()
可重入锁,同一个线程可以多次获取该锁,然后依次释放。有“递归”的概念。
示例1:
import threading
lock = threading.Lock()
rlock = threading.RLock()
## 使用threading.Lock()的情况,用于和RLock()对比
def test1():
print('I am lock')
lock.acquire()
print('I get Lock')
lock.acquire()
print('I get Lock 2')
lock.release()
lock.release()
def test2():
print('I am rlock')
rlock.acquire()
print('I get rLock')
rlock.acquire()
print('I get rLock 2')
rlock.release()
rlock.release()
执行test1()的时候,会出现死锁,程序只输出前两句print,而且程序永远不会停止。
执行test2()的时候,程序的3个print语句正常执行。
threading.Condition()
threading.Condition()默认自带一个RLock,另外还带有一个“等待池”,等待池中的线程处于阻塞状态。
threading.Condition()类带有的方法有:
acquire():获取Condition()自带的锁
release():释放锁
wait():该方法会释放锁,然后阻塞线程。
notify():该方法会唤醒“等待池”中线程。
notify_all():同notify(),但notify()只唤醒其中一个线程,而notify_all()唤醒所有线程。
注意:wait()方法会释放锁,而notify()和notify_all()不会释放锁。
示例1:
## 参考https://blog.youkuaiyun.com/luckytanggu/article/details/52183990
import threading
import time
# 商品
product = None
# 条件变量
con = threading.Condition()
# 生产者方法
def produce():
global product
if con.acquire():
while True:
if product is None:
print('produce...')
product = 'anything'
# 通知消费者,商品已经生产
con.notify()
# 等待通知
con.wait()
time.sleep(2)
# 消费者方法
def consume():
global product
if con.acquire():
while True:
if product is not None:
print('consume...')
product = None
# 通知生产者,商品已经没了
con.notify()
# 等待通知
con.wait()
time.sleep(2)
t1 = threading.Thread(target=produce)
t2 = threading.Thread(target=consume)
t2.start()
t1.start()
threading.Semaphore(value)
Semaphore()内置了一个计数器,每次调用acquire()的时候,value-1;每次调用release()的时候,value+1,当计数器的这个value为0时,它会阻塞,然后只能等待其他线程调用release()
示例1:
import threading
import time
s = threading.Semaphore(value = 3)
s.acquire()
print('I am 1')
s.acquire()
print('I am 2')
s.acquire()
print('I am 3')
s.acquire()
print('I am 4')
s.release()
s.release()
s.release()
s.release()
输出如下:
I am 1
I am 2
I am 3
程序不会自动停止,‘I am 4’,也永远不会打印
示例2:
import threading
import time
s = threading.Semaphore(value = 2)
l = threading.Lock()
def test():
#s.acquire() 去掉注释,或者把s改为l,看看输出效果有什么不一样
print(threading.current_thread().name)
time.sleep(2)
#s.release() 去掉注释,或者把s改为l,看看输出效果有什么不一样
t = [threading.Thread(target = test) for i in range(20)]
for i in t:
i.start()
threading.Event()
一个线程发出事件信号,其他线程等待它。
threading.Event()有一个内部标志,该标志可以用set()方法设置为True,用clear()方法重置为False,wait()方法会阻塞线程,直到标志位True
示例1:
import threading
e = threading.Event()
print(e.is_set())
e.set()
print(e.is_set())
e.clear()
print(e.is_set())
输出如下:
False
True
False
[Finished in 0.3s]
示例2:
import threading
import time
e = threading.Event()
def cooking():
print('I am cooking')
e.wait()
print('I finish cooking')
t = threading.Thread(target = cooking)
t.start()
print('I am sleeping')
time.sleep(3)
e.set()
输出如下:
I am cooking
I am sleeping
I finish cooking
[Finished in 3.2s]
threading.Timer()
这是一个计时器,这个类表示,经过一定的时间后运行的操作。
示例1:
import threading
def hello():
print('hello, world')
print('I will printing something')
t = threading.Timer(5, hello)
t.start()
# t.cancel() 在计时的过程中,这条语句会取消计时器的操作,被调用的函数不会执行。
threading.local()
threading.local()相当于一个字典,能够储存线程的特定属性。表示线程的局部数据。
示例1:
import threading
mydata = threading.local()
mydata.age = 12
print('age:', mydata.age)
def change():
mydata.car = 'BMW'
print(mydata.car)
t = threading.Thread(target = change)
t.start()
t.join()
#print('car:', mydata.car) #mydata.car只存在于特定的子线程内,去掉注释,会显示不存在mydata.car属性
print('age:', mydata.age)
输出如下:
age: 12
BMW
age: 12
[Finished in 0.2s]
示例2:
import threading
mydata = threading.local()
mydata.age = 12
print('age:', mydata.age)
def change2():
mydata.age = 'hello'
print(mydata.age)
t = threading.Thread(target = change2)
t.start()
t.join()
print('age:', mydata.age)
输出如下:
age: 12
hello
age: 12
[Finished in 0.2s]
可以看出,修改过的age属性,也只存在于子线程当中,在主线程中的age没有改变。
Lock,RLock,Condition,Semaphore,BoundedSemaphore对象都能够应用在with语句的上下文管理器中。
import threading
with some_lock:
# do something...
相当于:
some_lock.acquire():
try:
# do something...
finally:
some_lock.release()
参考文章:
https://docs.python.org/3/library/threading.html
http://bbs.51cto.com/thread-1349105-1.html
https://blog.youkuaiyun.com/luckytanggu/article/details/52183990
https://www.cnblogs.com/tkqasn/p/5700281.html