线程本地数据(threading.local)
线程本地数据,其实是创建一个全局的类似于字典的数据类型,让所有的线程在访问此字典的时候只能访问自己的数据,将线程之间的数据分隔开,避免冲突
import threading
local = threading.local()
def one():
local.a = 0
for i in range(100):
local.a += 1
print(local.a)
return
def two():
local.a = 0
for i in range(1000):
local.a += 2
print(local.a)
return
if __name__ == '__main__':
one = threading.Thread(target=one)
two = threading.Thread(target=two)
one.start()
two.start()
'''
100
2000
'''
条件对象(Condition)
条件对象本质上也是一把锁,可以是同步锁也可以是递归锁,可以自己传入,也可以自动创建,一般情况下,自动创建即可。条件对象不仅具有锁的功能,还可以实现简单的线程间通信。
把条件对象当做普通的锁来使用:
import threading
con = threading.Condition()
num = 0
class Consumer(threading.Thread):
def run(self):
global num
for i in range(10):
con.acquire()
num += 1
con.release()
class Producer(threading.Thread):
def run(self):
global num
for i in range(10):
con.acquire()
num += 1
con.release()
if __name__ == '__main__':
for i in range(10):
consumer = Consumer()
producer = Producer()
consumer.start()
producer.start()
consumer.join()
producer.join()
print(num)
条件对象显然不是直接当成锁来使用的,条件对象的关键在于实现了线程间的简单通信
简单的生产者消费者模型:
import threading
con = threading.Condition()
num = 0
class Consumer(threading.Thread):
def run(self):
global num
con.acquire()
con.wait()
num -= 1
print(f'{self.ident} -1')
con.notify()
con.release()
class Producer(threading.Thread):
def run(self):
global num
con.acquire()
num += 1
print(f'{self.ident} +1')
con.notify()
con.wait()
con.release()
if __name__ == '__main__':
for i in range(10):
consumer = Consumer()
producer = Producer()
consumer.start()
producer.start()
consumer.join()
producer.join()
'''
11624 +1
3124 -1
10128 +1
11440 -1
7328 +1
11876 -1
5272 +1
12216 -1
6612 +1
12280 -1
10024 +1
1428 -1
10452 +1
12016 -1
292 +1
12112 -1
9420 +1
6296 -1
6332 +1
2840 -1
'''
创建20个线程,10个生产者,10个消费者。一个生产者对num的值+1,一个消费者对num的值-1。
方法 | 描述 |
---|---|
wait() | 阻塞调用此方法的线程。 |
notify(n=1) | 通知阻塞了的线程,继续运行。n表示最多通知多少个线程。此方法并不会释放锁,释放锁还是需要release()。 |
notify_all() | 唤醒所有等待的线程 |
acquire() | 获得锁 |
release() | 释放锁 |
信号量对象(Semaphore)
信号量的本质也是锁,信号量对象内部维护着一个计数器,每当调用acquire()方法,计数器的值就会-1,每当调用release()方法,计数器的值就会+1。特别之处在于,计数器的不能为负数,当某个线程调用acquire()方法,发现计数器的值为0,就会阻塞,默认值为1。计数器初始值的作用就是同一时刻允许的最大线程运行数。
用信号量来实现生产者消费者模型:
import threading
import time
# 设置初始的计数器值
semaphore = threading.Semaphore(value=5)
num = 0
class Consumer(threading.Thread):
def run(self):
global num
semaphore.acquire()
num -= 1
print(f'{self.ident} -1')
semaphore.release()
class Producer(threading.Thread):
def run(self):
global num
time.sleep(2)
semaphore.acquire()
num += 1
print(f'{self.ident} +1')
semaphore.release()
if __name__ == '__main__':
for i in range(10):
consumer = Consumer()
producer = Producer()
consumer.start()
producer.start()
consumer.join()
producer.join()
事件对象(Event)
这是线程之间通信的最简单机制之一:一个线程发出事件信号,而其他线程等待该信号。一个事件对象管理一个内部标识,调用 set()方法可将其设置为 true ,调用 clear()方法可将其设置为 false ,调用 wait() 方法将进入阻塞直到标识为 true 。
import threading
import time
event = threading.Event()
class Task(threading.Thread):
def run(self):
event.wait()
print(f'{self.ident} 收到通知')
if __name__ == '__main__':
for i in range(5):
task = Task()
task.start()
time.sleep(5)
event.set()
"""
10680 收到通知
11076 收到通知
600 收到通知
11672 收到通知
8912 收到通知
"""
主线程将内部的标识符改为False,调用了wait()方法的线程就会停止阻塞,开始执行任务。
栅栏对象(Barrier)
栅栏对象用于实现多个线程间的同步,栅栏对象有内置的wait()方法,调用该方法,会使当前的线程阻塞,但是一旦在所有的线程都调用了wait()方法,则会自动解除阻塞。
import threading
# 实例化栅栏对象,并设置最多同步多少个线程
barrier = threading.Barrier(2)
def one():
barrier.wait()
print('one')
def two():
barrier.wait()
print('two')
o = threading.Thread(target=one)
t = threading.Thread(target=two)
o.start()
t.start()
o.join()
t.join()
'''
one
two
'''
定时器对象(Timer)
定时器对象就是可定时执行任务,其本质就是一个阻塞一定时长的线程。
import threading
def show():
print('time out!!!')
timer = threading.Timer(10, show)
timer.start()