线程间通信
进程和线程
start()函数用于启动线程或进程
join(timeout)函数用于阻塞主进程或主线程,等子线程或进程结束后,主线程或进程才结束。(当设置setDaemon(True)守护进程时,如果等待timeout时间,主进程会杀死子进程,如果没有设置守护进程,主进程等待timeout时间结束,子进程继续执行)
start(),join()不能再一个在一个循环里连用。
#错误代码,因为start()启动第一个子线程,然后join()函数直接阻塞主线程,等第一个子线程执行完才能开始第二个子线程。相当于顺序执行。
from threading import Thread
threads = [Thread() for i in range(5)]
for thread in threads:
thread.start()
thread.join()
#正确代码
from threading import Thread
threads = [Thread() for i in range(5)]
for thread in threads:
thread.start()
for thread in threads:
thread.join()
为了保证线程的安全和线程同步(访问同一变量)。需要一些信息对象来进行线程间的通信。如
Lock,RLock():基准锁,可重入锁对象。当多个线程访问同一变量,为了保证数据同步,需要对共享变量加锁。
import threading, time
from threading import Thread,RLock,Event,Condition
#锁对象,线程通讯,当多个线程共享一个资源时,需要加锁,保持资源同步
class Account(object):
"账户类"
def __init__(self,name,balance=0):
self.name = name
self.balance = balance
self.rlock = RLock()
def draw_money(self,money):
"存钱"
with self.rlock:
new_balance = self.balance + money
time.sleep(1)
self.balance = new_balance
print('{} 的 balance 是 {}'.format(self.name,self.balance))
def main():
account = Account('sungc')
thread_lst = []
for i in range(10):
t = Thread(target=account.draw_money,args=(100,))
t.start()
thread_lst.append(t)
for one in thread_lst:
one.join()
if __name__=='__main__':
main()
Condition
Condition(Lock):条件变量对象。将锁用于改变共享变量的状态。对状态感兴趣的线程,一直循环重复调用wait()方法直到状态发生。对于修改状态的线程,他将当前状态修改为等待者所期望的状态后, 调用notify() 方法或者 notify_all() 方法唤醒一个等待者。
from time import sleep
import threading, time
class Account(object):
"""银行账户"""
def __init__(self, balance=0):
self.balance = balance
lock = threading.Lock()
self.condition = threading.Condition(lock)
def withdraw(self, money):
"""取钱"""
with self.condition:
while money > self.balance:
print('钱不够,存款:{},取钱:{}'.format(self.balance, money))
self.condition.wait()
new_balance = self.balance - money
self.balance = new_balance
print('钱够,取款:{},存款:{}'.format(money, self.balance))
def deposit(self, money):
"""存钱"""
with self.condition:
new_balance = self.balance + money
self.balance = new_balance
print('存钱:{},存款共:{}'.format(money, self.balance))
self.condition.notify_all()
sleep(0.5)
def main():
account = Account()
t_withdraw = threading.Thread(target=account.withdraw,args=(40,))
t_withdraw.start()
for i in range(5):
time.sleep(0.5)
t = threading.Thread(target=account.deposit,args = (10,))
t.start()
if __name__=='__main__':
main()
Semaphore
Semaphore(value=1),BoundedSemaphore:信息量对象,有界信息量对象。信息量对象通常用于保护数量有限的资源,如数据库服务器。在资源数量有限的情况下都应该使用信息量对象。初始值默认为1,调用acquire()方法,获取一个信息号量。当value值大于0的情况下,value减去1,当value为0时,阻塞并唤醒release()方法。release()方法释放一个信息号量。value的值加1.
多个线程竞争一个资源–保护临界资源–锁(Lock,RLock)
多个线程竞争多个资源(线程数>资源数)–记录线程的申请数量,释放数量,保证资源数量大于0–信息量(Semaphore)
多个线程调度—暂停线程执行/唤醒等待中的线程—条件变量对象(Condition)
max_connection = 10
pool_sema = BoundedSemaphore(max_connection)
with pool_sema:
conn = connectdb()
try:
#use connection
finally
conn.close()
Event
Event():事件对象。一个线程发出时间事件对象信号,其他线程等待事件对象信号。事件对象管理一个内部标志。set()方法将内部标志设置为True,clear()方法将内部标志设置为False。wait()方法阻塞,等待内部标志变为True。
class Account(object):
"账户类"
def __init__(self,name,event,balance=0):
self.name = name
self.balance = balance
self.rlock = RLock()
self.event = event
def draw_money(self,money):
"存钱"
print('{}开始存钱,开始时间{}'.format(self.name, time.ctime(time.time())))
self.event.wait()
with self.rlock:
new_balance = self.balance + money
time.sleep(1)
self.balance = new_balance
print('{} 的 balance 是 {}'.format(self.name,self.balance))
print('{}存钱结束,结束时间{}'.format(self.name, time.ctime(time.time())))
#event对象,一个线程发出事件信号,其他线程等待信号,包含三个方法clear()将内部标志设置为false,wait()阻塞线程执行,直至内部标注为True,set()将内部标志设置为True
def main():
event = Event()
#重置event,内部标志设置为false,阻塞wait()方法
event.clear()
account = Account('sungc',event)
thread_lst = []
for i in range(10):
t = Thread(target=account.draw_money,args=( 100,))
t.start()
thread_lst.append(t)
print('等待5秒')
time.sleep(2)
event.set()
if __name__=='__main__':
main()
Timer():定时器对象。他表示一个操作应该等待一段时间后执行。相当于一个定时器。调用start()方法启动定时器。clear()停止定时器(在定时器结束之前停止)。
from threading import Timer
def hello(str_temp):
print('hello ' + str_temp)
t = Timer(30,hello,'world')
t.start()#after 30 seconds, "hello, world" will be printed
Queue
从一个线程向另一个线程发送数据最安全的方式可能就是使用 queue 库中的队列了。创建一个被多个线程共享的 Queue 对象,这些线程通过使用put() 和 get() 操作来向队列中添加或者删除元素。
from queue import Queue
import threading, time
class Student(threading.Thread):
"学生类(线程)"
def __init__(self,name,queue):
super().__init__()
self.name = name
self.queue = queue
def run(self):
while True:
name_str = self.queue.get()
if name_str != None:
if name_str==self.name:
print("{}:到!".format(self.name))
break
else:
break
class Teacher(threading.Thread):
"老师类(线程)"
def __init__(self,name,queue,student_name_lst):
super().__init__()
self.name = name
self.queue = queue
self.student_name_lst = student_name_lst
def run(self):
"点名"
for one in self.student_name_lst:
print("老师{}:{}来了没?".format(self.name,one))
self.queue.put(one)
time.sleep(0.5)
self.queue.put(None) #发送None,点名结束
print('点名结束')
def main():
queue = Queue()
teacher = Teacher('Pony',queue,['sungc','wangdr','lilei'])
teacher.start()
student = Student('sungc',queue)
student.start()
student2 = Student('wangdr', queue)
student2.start()
student3 = Student('lilei', queue)
student3.start()
if __name__=='__main__':
main()
线程池和进程池
线程的创建和销毁是需要消耗时间的资源的。我们可以创建一个max_workers大小的线程池,当有大于max_workers的任务需要启动的时候,只启动max_workers个线程,执行max_workers个任务。其他任务排队等待。等其中一个任务执行完毕,可以安排新任务给这个线程执行。python提供了concurrent.futures模块,其中有ThreadPoolExecutor和ProcessPoolExecutor,帮助我们自动调度线程和进程。
ThreadPoolExecutor(max_workers) | 创建线程池,最多为max_works个线程 |
submit(func,*args, **kwargs) | 向线程池提交需要执行的任务(函数和参数),返回该任务的句柄 |
cancel() | 通过任务句柄取消任务,如果任务在线程池中则取消失败,返回False。如果任务在排队等待,取消成功,返回True |
result() | 通过任务句柄获取任务返回值 |
done() | 通过任务句柄查看任务状态 |
as_completed(fs) | 参数是任务句柄列表,当任务执行完毕后返回任务句柄,(先完成先返回) |
map(fn,iters) | 将iter序列中每个元素都执行fn函数,添加到线程池中,执行完毕返回句柄列表 |
shutdown() | 等待线程池中所有任务执行完毕,返回True |
通常submit()和as_completed()或者shutdown()结合使用。
from concurrent.futures import ThreadPoolExecutor,as_completed
from threading import Lock
import time
class Account(object):
"账户"
def __init__(self,balance=0):
self._balance = balance
self._lock = Lock()
@property
def balance(self):
return self._balance
def change(self,money):
"存钱"
with self._lock:
time.sleep(0.1)
self._balance += money
return self._balance
def main():
acount = Account()
with ThreadPoolExecutor(max_workers=10) as te:
task_lst = []
for i in range(30):
task = te.submit(acount.change,1)
task_lst.append(task)
for one_task in as_completed(task_lst):
print(one_task.result())
print('*' * 50)
te.shutdown()
print(acount.balance)
if __name__=='__main__':
main()
链接: https://www.cnblogs.com/wongbingming/p/9035579.html
链接: https://github.com/jackfrued/Python-100-Days/blob/master/Day16-20/16-20.Python%E8%AF%AD%E8%A8%80%E8%BF%9B%E9%98%B6.md
链接: https://www.liaoxuefeng.com/wiki/1016959663602400/1017970488768640
链接: https://www.cnblogs.com/dancesir/p/9171677.html