线程通信(Lock,RLock,Condition,Semaphore,Event,Queue)

线程间通信

进程和线程
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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值