【python】python中的多线程编程方法
部分参考来源
http://blog.sina.com.cn/s/blog_5a2bbc860101gedc.html
https://www.cnblogs.com/liliuguang/p/9510940.html
https://blog.csdn.net/wh_sjc/article/details/70283843
https://juejin.im/post/5d515c7551882511ed7c273c
https://www.cnblogs.com/Allen-rg/p/7172958.html
https://www.cnblogs.com/fmqdblog/p/10680077.html
https://blog.csdn.net/jiduochou963/article/details/88020415
什么是进程
进程是资源分配的最小单元。进程是,可并发执行的程序,在某个数据集合上的一次计算活动,它是一个动态的概念。而,程序是指令的有序集合,其本身没有任何运行的含义,是一个静态的概念。程序可以作为一种软件资料长期存在,而进程是有一定生命期的。程序是永久的,进程是暂时的。
进程的特性:
- 动态性:进程的实质是程序在多道程序系统中的一次执行过程,进程是动态产生,动态消亡的
- 并发性:任何进程都可以同其他进程一起并发执行
- 独立性:进程是一个能独立运行的基本单位,同时也是系统分配资源和调度的独立单位
- 异步性:由于进程间的相互制约,使进程具有执行的间断性,即进程按各自独立的、不可预知的速度向前推进
进程的结构特征:进程由程序、数据和进程控制块三部分组成
另外,多个不同的进程可以包含相同的程序,一个程序在不同的数据集里就构成不同的进程,能得到不同的结果,但是执行过程中,程序不能发生改变。
并发,和,并行,的区别:
- 并发:当有多个进程(或线程)在操作时,如果系统只有一个CPU,则它根本不可能真正同时进行一个以上的进程(或线程),它只能把CPU运行时间划分成若干个时间段,再将时间段分配给各个进程(或线程)执行,在一个时间段的进程(或线程)代码运行时,其它进程(或线程)处于挂起状。这种方式称之为并发(Concurrent)
- 并行:当系统有一个以上CPU时,则进程(或线程)的操作有可能非并发。当一个CPU执行一个进程(或线程)时,另一个CPU可以执行另一个进程(或线程),两个进程(或线程)互不抢占CPU资源,可以同时进行,这种方式称之为并行(Parallel)
进程之间的通信方式:
- 管道:有匿名管道和命名管道两种,匿名管道只能用于具有亲缘关系的进程之间的通信,命名管道可以在无关的进程之间交换数据
- 消息队列
- 内存共享:指两个或多个进程共享一个给定的存储区,是最快的进程之间的通信方式。存在多进程竞争内存的问题
- 信号量:本质就是一个计数器,用来实现进程之间的互斥与同步。信号量+内存共享,解决多进程竞争内存的问题
- Socket
进程之间的同步方式:
- 信号量:在信号量上只有三种操作可以进行:初始化,P操作和V操作,这三种操作都是原子操作。P操作(递减操作)可以用于阻塞一个进程,V操作(增加操作)可以用于解除阻塞一个进程。原子操作是指不会被调度机制打断的操作,这种操作一旦开始,就一直运行到结束
- 管程
- 消息传递
什么是线程
线程是任务执行的最小单元。一个进程可以有多个线程。线程共享进程的所有资源。线程上下文切换比进程上下文切换要快得多
线程之间的通信方式:
- 锁机制:包括互斥锁、条件变量、读写锁
- 互斥锁:提供了以排他方式防止数据结构被并发修改的方法
- 读写锁:允许多个线程同时共享数据,而对写操作是互斥的
- 条件变量:可以以原子的方式阻塞进程,直到某个特定条件为真为止。对条件的测试是在互斥锁的保护下进行的。条件变量始终与互斥锁一起使用
- 信号量机制(Semaphore):包括无名线程信号量和命名线程信号量
- 信号机制(Signal):类似进程间的信号处理
线程间的通信目的主要是用于线程同步,所以线程没有像进程通信中的用于数据交换的通信机制
python中的多线程编程方法
主要转载自https://blog.csdn.net/jiduochou963/article/details/88020415,理论总结的很清晰,程序示例很好,感谢原博博主的工作。
简单介绍多线程编程
- 示例一
# -*- coding:utf-8 -*-
from time import sleep, ctime
def loop0():
print('start loop 0 at:', ctime())
sleep(4)
print('loop 0 done at:', ctime())
def loop1():
print('start loop 1 at:', ctime())
sleep(2)
print('loop 1 done at:', ctime())
def main():
print('starting at:', ctime())
loop0()
loop1()
print('all DONE at:', ctime())
if __name__ == '__main__':
main()
程序输出如下:
starting at: Mon Feb 24 16:19:34 2020
start loop 0 at: Mon Feb 24 16:19:34 2020
loop 0 done at: Mon Feb 24 16:19:38 2020
start loop 1 at: Mon Feb 24 16:19:38 2020
loop 1 done at: Mon Feb 24 16:19:40 2020
all DONE at: Mon Feb 24 16:19:40 2020
可见,66当不使用多线程编程时,程序按顺序先执行函数loop0,执行完成后,再执行函数loop1。
- 示例二
_thread库,锁,的使用方法:
函数/方法 | 说明 |
---|---|
_thread.start_new_thread(function, args, kwargs=None) | 派生一个新的线程,使用给定的 args 和可选的 kwargs 来执行 function |
_thread.allocate_lock() | 分配 LockType 锁对象 |
_thread.exit() | 给线程退出指令 |
_thread.LockType | 锁对象的方法 |
_thread.acquire(wait=None) | 尝试获取锁对象 |
_thread.locked() | 如果获取了锁对象则返回 True,否则,返回 False |
_thread.release() | 释放锁 |
# -*- coding:utf-8 -*-
import _thread as thread
from time import sleep, ctime
def loop0():
print('start loop 0 at:', ctime())
sleep(4)
print('loop 0 done at:', ctime())
def loop1():
print('start loop 1 at:', ctime())
sleep(2)
print('loop 1 done at:', ctime())
def main():
print('starting at:', ctime())
thread.start_new_thread(loop0, ())
thread.start_new_thread(loop1, ())
sleep(6)
print('all DONE at:', ctime())
if __name__ == '__main__':
main()
程序输出如下:
starting at: Mon Feb 24 17:02:33 2020
start loop 1 at: Mon Feb 24 17:02:33 2020
start loop 0 at: Mon Feb 24 17:02:33 2020
loop 1 done at: Mon Feb 24 17:02:35 2020
loop 0 done at: Mon Feb 24 17:02:37 2020
all DONE at: Mon Feb 24 17:02:39 2020
可见两个子线程loop0个loop1同时开始执行,这里加sleep(6)用来作为同步机制,因为,_thread 模块对于进程何时退出没有控制,当主线程结束时,所有其他线程也都强制结束
- 示例三
# -*- coding:utf-8 -*-
import _thread as thread
from time import sleep, ctime
loops = [4, 2]
def loop(nloop, nsec, lock):
print('start loop', nloop, 'at:', ctime())
sleep(nsec)
print('loop', nloop, 'done at:', ctime())
lock.release()
def main():
print('starting at', ctime())
locks = []
nloops = range(len(loops))
for i in nloops:
lock = thread.allocate_lock() # 得到锁对象
lock.acquire() # 取得锁,效果等同于将锁锁上
locks.append(lock) # 将锁添加到锁列表中
for i in nloops:
thread.start_new_thread(loop, (i, loops[i], locks[i]))
for i in nloops:
while locks[i].locked():
pass
print('all DONE at:', ctime())
if __name__ == '__main__':
main()
程序输出如下:
starting at Mon Feb 24 17:16:19 2020
start loop 0 at: Mon Feb 24 17:16:19 2020
start loop 1 at: Mon Feb 24 17:16:19 2020
loop 1 done at: Mon Feb 24 17:16:21 2020
loop 0 done at: Mon Feb 24 17:16:23 2020
all DONE at: Mon Feb 24 17:16:23 2020
可见子进程loop0和loop1同样一起开始执行,但是这种锁的使用使线程的同步更加合理
threading库
相比_thread库,threading库支持,守护线程,这一概念。
守护线程的工作方式是:守护线程一般是一个等待客户端请求服务的服务器。如果没有客户端请求,守护线程就是空闲的。如果把一个线程设置为守护线程,就表示这个线程是不重要的,进程退出时不需要等待这个线程执行完成。如果主线程准备退出时,不需要等待某些子线程完成,就可以为这些子线程设置守护线程标记。该标记值为真时,表示该线程是不重要的,或者说该线程只是用来等待客户端请求而不做任何其他事情。
要将一个线程设置为守护线程,需要在启动线程之前执行如下赋值语句:Thread.daemon = True,同样,要检查线程的守护状态,也只需要检查这个值即可。一个新的子线程会继承父线程的守护标记。整个 python 程序(主线程)将在所有非守护线程退出之后才退出,换句话说,就是没有剩下存活的非守护线程时。
threading 模块的类:
类 | 描 述 |
---|---|
Thread | 表示一个执行线程的对象 |
Lock | 锁原语对象(和 _thread 模块中的锁一样) |
RLock | 可重入锁对象,使单一线程可以(再次)获得已持有的锁(递归锁) |
Condition | 条件变量对象,使得一个线程等待另一个线程满足特定的“条件”,比如改变状态或某个数据值 |
Event | 条件变量的通用版本,任意数量的线程等待某个事件的发生,在该事件发生后所有线程将被激活 |
Semaphore | 为线程间共享的有限资源提供了一个“计数器”,如果没有可用资源时会被阻塞 |
BoundedSemaphore | 与 Semaphore 相似,不过它不允许超过初始值 |
Timer | 与 Thread 相似,不过它要在运行前等待一段时间 |
Barrier | 创建一个“障碍”,必须达到指定数量的线程后才可以继续 |
Thread类
Thread类属性 | 描述 |
---|---|
name | 线程名 |
ident | 线程的标识符 |
daemon | 布尔标志,表示这个线程是否是守护线程 |
Thread类方法 | 描述 |
---|---|
_init_(group=None, tatget=None, name=None, args=(),kwargs ={}, verbose=None, daemon=None) | 实例化一个线程对象,需要有一个可调用的 target,以及其参数 args或 kwargs。还可以传递 name 或 group 参数,不过后者还未实现。此外 , verbose 标 志 也 是 可 接 受 的。 而 daemon 的 值 将 会 设定thread.daemon 属性/标志 |
start() | 开始执行该线程 |
run() | 定义线程功能的方法(通常在子类中被应用开发者重写) |
join(timeout=None) | 直至启动的线程终止之前一直挂起;除非给出了 timeout(秒),否则会一直阻塞 |
is_alive() | 布尔标志,表示这个线程是否还存活 |
使用Thread 类,创建线程的三种方法:
- 创建Thread 的实例,传给它一个函数
# -*- coding:utf-8 -*-
import threading
from time import sleep, ctime
loops = [4, 2]
def loop(nloop, nsec):
print('start loop', nloop, 'at:', ctime())
sleep(nsec)
print('loop', nloop, 'done at:', ctime())
def main():
print('starting at', ctime())
threads = []
nloops = range(len(loops))
for i in nloops:
t = threading.Thread(target=loop, args=(i, loops[i])) # args是调用target的参数
threads.append(t)
for i in nloops:
threads[i].start()
for i in nloops:
threads[i].join()
print('all DONE at:', ctime())
if __name__ == '__main__':
main()
程序输出如下:
starting at Mon Feb 24 19:41:21 2020
start loop 0 at: Mon Feb 24 19:41:21 2020
start loop 1 at: Mon Feb 24 19:41:21 2020
loop 1 done at: Mon Feb 24 19:41:23 2020
loop 0 done at: Mon Feb 24 19:41:25 2020
all DONE at: Mon Feb 24 19:41:25 2020
实例化 Thread和调用 _thread.start_new_thread()的最大区别是,新线程不会立即开始执行。这是一个非常有用的同步功能,尤其是当你并不希望线程立即开始执行时。
当所有线程都分配完成之后,通过调用每个线程的 start()方法让它们开始执行,而不是在这之前就会执行。相比于管理一组锁(分配、获取、释放、检查锁状态等)而言,这里只需要为每个线程调用 join()方法即可。
join()方法将等待线程结束,或者在提供了超时时间的情况下,达到超时时间。使用 join()方法要比等待锁释放的无限循环更加清晰(这也是这种锁又称为自旋锁的原因)。
对于 join()方法而言,其另一个重要方面是其实它根本不需要调用。一旦线程启动,它们就会一直执行,直到给定的函数完成后退出。如果主线程还有其他事情要去做,而不是等待这些线程完成(例如其他处理或者等待新的客户端请求),就可以不调用 join()。 join()方法只有在你需要等待线程完成的时候才是有用的。
- 创建Thread 的实例,传给它一个可调用的类实例
# -*- coding:utf-8 -*-
import threading
from time import sleep, ctime
loops = [4, 2]
class ThreadFunc(object):
def __init__(self, func, args):
self.func = func
self.args = args
def __call__(self):
self.func(*self.args) # 拆包
def loop(nloop, nsec):
print('start loop', nloop, 'at:', ctime())
sleep(nsec)
print('loop', nloop, 'done at:', ctime())
def main():
print('starting at', ctime())
threads = []
nloops = range(len(loops))
for i in nloops:
t = threading.Thread(target=ThreadFunc(loop, (i, loops[i])))
threads.append(t)
for i in nloops:
threads[i].start()
for i in nloops:
threads[i].join()
print('all DONE at:', ctime())
if __name__ == '__main__':
main()
程序输出如下:
starting at Mon Feb 24 21:18:50 2020
start loop 0 at: Mon Feb 24 21:18:50 2020
start loop 1 at: Mon Feb 24 21:18:50 2020
loop 1 done at: Mon Feb 24 21:18:52 2020
loop 0 done at: Mon Feb 24 21:18:54 2020
all DONE at: Mon Feb 24 21:18:54 2020
当创建新线程时,Thread 类的代码将调用ThreadFunc对象,此时会调用__call__()这个特殊方法。由于我们已经有了要用到的参数,这里就不需要再将其传递给Thread()的构造函数了,直接调用即可。
- 派生Thread 的子类,并创建子类的实例
# -*- coding:utf-8 -*-
import threading
from time import sleep, ctime
loops = [4, 2]
class MyThread(threading.Thread):
def __init__(self, func, args):
threading.Thread.__init__(self)
self.func = func
self.args = args
def run(self):
self.func(*self.args)
def loop(nloop, nsec):
print('start loop', nloop, 'at:', ctime())
sleep(nsec)
print('loop', nloop, 'done at:', ctime())
def main():
print('starting at', ctime())
threads = []
nloops = range(len(loops))
for i in nloops:
t = MyThread(loop, (i, loops[i]))
threads.append(t)
for i in nloops:
threads[i].start()
for i in nloops:
threads[i].join()
print('all DONE at:', ctime())
if __name__ == '__main__':
main()
程序输出如下:
starting at Tue Feb 25 11:03:04 2020
start loop 0 at: Tue Feb 25 11:03:04 2020
start loop 1 at: Tue Feb 25 11:03:04 2020
loop 1 done at: Tue Feb 25 11:03:06 2020
loop 0 done at: Tue Feb 25 11:03:08 2020
all DONE at: Tue Feb 25 11:03:08 2020
Lock类
当多线程中需要“独占资源”的时候,要使用锁来控制,防止多个线程同时占用资源而出现其他异常。
当多线程争夺锁时,允许第一个获得锁的线程进入临界区,并执行代码。所有之后到达的线程将被阻塞,直到第一个线程执行结束,退出临界区,并释放锁。此时,其他等待的线程可以获得锁并进入临界区。不过请记住,那些被阻塞的线程是没有顺序的(即不是先到先执行),胜出线程的选择是不确定的,而且还会根据 python 实现的不同而有所区别。
类方法 | 描述 |
---|---|
acquire() | 获得锁 |
release() | 释放锁 |
# -*- coding:utf-8 -*-
import threading
from time import sleep, ctime
lock = threading.Lock()
l = []
def test(n):
print("id:", n, " Start time:", ctime())
lock.acquire()
l.append(n)
print(l)
sleep(2)
print("id:", n, " End time:", ctime())
lock.release()
def main():
for i in range(0, 10):
th = threading.Thread(target=test, args=(i, ))
th.start()
if __name__ == '__main__':
main()
程序输出如下:
id: 0 Start time: Tue Feb 25 11:55:31 2020
[0]
id: 1 Start time: Tue Feb 25 11:55:31 2020
id: 2 Start time: Tue Feb 25 11:55:31 2020
id: 3 Start time: Tue Feb 25 11:55:31 2020
id: 4 Start time: Tue Feb 25 11:55:31 2020
id: 5 Start time: Tue Feb 25 11:55:31 2020
id: 6 Start time: Tue Feb 25 11:55:31 2020
id: 7 Start time: Tue Feb 25 11:55:31 2020
id: 8 Start time: Tue Feb 25 11:55:31 2020
id: 9 Start time: Tue Feb 25 11:55:31 2020
id: 0 End time: Tue Feb 25 11:55:33 2020
[0, 1]
id: 1 End time: Tue Feb 25 11:55:35 2020
[0, 1, 2]
id: 2 End time: Tue Feb 25 11:55:37 2020
[0, 1, 2, 3]
id: 3 End time: Tue Feb 25 11:55:39 2020
[0, 1, 2, 3, 4]
id: 4 End time: Tue Feb 25 11:55:41 2020
[0, 1, 2, 3, 4, 5]
id: 5 End time: Tue Feb 25 11:55:43 2020
[0, 1, 2, 3, 4, 5, 6]
id: 6 End time: Tue Feb 25 11:55:45 2020
[0, 1, 2, 3, 4, 5, 6, 7]
id: 7 End time: Tue Feb 25 11:55:47 2020
[0, 1, 2, 3, 4, 5, 6, 7, 8]
id: 8 End time: Tue Feb 25 11:55:49 2020
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
id: 9 End time: Tue Feb 25 11:55:51 2020
可见,加了锁之后,当多个线程都要往列表中写入数据的时候,只有当一个线程写入完成后,再执行下一个线程。
信号量示例
对于拥有有限资源的应用来说,使用信号量实现线程同步是个不错的决定。
信号量是最古老的同步原语之一。它是一个计数器,当资源消耗时递减,当资源释放时递增。你可以认为信号量代表它们的资源可用或不可用。消耗资源使计数器递减的操作习惯上称为 P,也称为 wait、try、acquire、pend 或 procure。相对地,当一个线程对一个资源完成操作时,该资源需要返回资源池中。这个操作一般称为 V,也称为 signal、 increment、 release、 post、 vacate。
python 简化了所有的命名,使用和锁的函数/方法一样的名字: acquire 和 release。信号量比锁更加灵活,因为可以有多个线程,每个线程拥有有限资源的一个实例。
BoundedSemaphore类
类方法 | 描述 |
---|---|
acquire | 信号量P操作,计数器减一 ,但如果低于零,会一直阻塞。所以通过传入非阻塞的标志 False(acquire(False)),让调用不再阻塞,而在应当阻塞的时候返回一个False,指明没有更多的资源了 |
release | 信号量V操作,计数器加一,但如果超过初始值,报错ValueError |
在下面的例子中,我们将模拟一个简化的糖果机。这个特制的机器只有 5 个可用的槽来保持库存(糖果)。如果所有的槽都满了,糖果就不能再加到这个机器中了;相似地,如果每个槽都空了,想要购买的消费者就无法买到糖果了。我们可以使用信号量来跟踪这些有限的资源(糖果槽)
# -*- coding:utf-8 -*-
from atexit import register
from random import randrange
from threading import BoundedSemaphore, Lock, Thread
from time import sleep, ctime
lock = Lock()
MAX = 5
candytray = BoundedSemaphore(MAX)
def refill():
lock.acquire()
print("Refilling candy...", end="")
try:
candytray.release()
except ValueError:
print("full, skipping")
else:
print("OK")
lock.release()
def buy():
lock.acquire()
print("Buying candy...", end="")
if candytray.acquire(False):
print("OK")
else:
print("empty, skipping")
lock.release()
def producer(loops):
for i in range(loops):
refill()
sleep(randrange(3))
def consumer(loops):
for i in range(loops):
buy()
sleep(randrange(3))
def main():
print("Starting at:", ctime())
nloops = randrange(2, 6)
print("The Candy Machine (full with C%d bars)!" % MAX)
Thread(target=consumer, args=(randrange(nloops, nloops + MAX + 2), )).start()
Thread(target=producer, args=(nloops, )).start()
@register
def atexit():
print("all DONE at:", ctime())
if __name__ == "__main__":
main()
程序输出如下:
Starting at: Tue Feb 25 15:52:09 2020
The Candy Machine (full with C5 bars)!
Buying candy…OK
Refilling candy…OK
Buying candy…OK
Buying candy…OK
Refilling candy…OK
Refilling candy…OK
Refilling candy…full, skipping
Buying candy…OK
Buying candy…OK
all DONE at: Tue Feb 25 15:52:15 2020
生产者消费者问题
我们使用 queue 模块来提供线程间通信的机制,从而让线程之间可以互相分享数据。具体而言,就是创建一个队列,让生产者(线程)在其中放入新的商品,而消费者(线程)消费这些商品
queue模块
queue模块是python一个独立的第三方库,而不是threading的一个类
模块的类 | 描述 |
---|---|
Queue(maxsize=0) | 创建一个先入先出队列。如果给定最大值,则在队列没有空间时阻塞;否则(没有指定最大值),为无限队列 |
LifoQueue(maxsize=0) | 创建一个后入先出队列。如果给定最大值,则在队列没有空间时阻塞;否则(没有指定最大值),为无限队列 |
PriorityQueue(maxsize=0) | 创建一个优先级队列。如果给定最大值,则在队列没有空间时阻塞;否则(没有指定最大值),为无限队列 |
类方法 | 描述 |
---|---|
qsize() | 返回队列大小,注意是队列大小而不是队列容量 |
empty() | 如果队列为空,则返回 True;否则,返回 False |
full() | 如果队列已满,则返回 True;否则,返回 False |
put (item, block=Ture, timeout=None) | 将 item 放入队列。如果 block 为 True(默认)且 timeout 为 None,则在有可用空间之前阻塞;如果 timeout 为正值,则最多阻塞 timeout 秒;如果 block 为 False,则抛出 Empty 异常 |
put_nowait(item) | 和 put(item, False)相同 |
get (block=True, timeout=None) | 从队列中取得元素。如果给定了 block(非 0),则一直阻塞到有可用的元素为止 |
get_nowait() | 和 get(False)相同 |
task_done() | 于表示队列中的某个元素已执行完成,该方法会被下面的 join()使用 |
join() | 在队列中所有元素执行完毕并调用上面的 task_done()信号之前,保持阻塞 |
异常 | 描述 |
---|---|
Empty | 当对空队列调用 get*()方法时抛出异常 |
Full | 当对已满的队列调用 put*()方法时抛出异常 |
# -*- coding:utf-8 -*-
import threading
from time import sleep
from random import randint
from queue import Queue
lock = threading.Lock()
class MyThread(threading.Thread):
def __init__(self, func, args):
threading.Thread.__init__(self)
self.func = func
self.args = args
def run(self):
self.func(*self.args)
def writeQ(queue):
lock.acquire()
print("producing object for Q...", end="")
queue.put("xxx", 1)
print("size now", queue.qsize())
lock.release()
def readQ(queue):
lock.acquire()
val = queue.get(1)
print("consumed object from Q... size now", queue.qsize())
lock.release()
def writer(queue, loops):
for i in range(loops):
writeQ(queue)
sleep(randint(1, 3))
def reader(queue, loops):
for i in range(loops):
readQ(queue)
sleep(randint(2, 5))
funcs = [writer, reader]
nfuncs = range(len(funcs))
def main():
nloops = randint(2, 5)
q = Queue(32)
threads = []
for i in nfuncs:
t = MyThread(funcs[i], (q, nloops))
threads.append(t)
for i in nfuncs:
threads[i].start()
for i in nfuncs:
threads[i].join()
print("all DONE")
if __name__ == "__main__":
main()
程序输出如下:
producing object for Q…size now 1
consumed object from Q… size now 0
producing object for Q…size now 1
producing object for Q…size now 2
consumed object from Q… size now 1
producing object for Q…size now 2
consumed object from Q… size now 1
consumed object from Q… size now 0
all DONE
条件变量示例
Condition(条件变量)通常与一个锁关联。可以认为,除了Lock带有的锁定池外,Condition还包含一个等待池,池中的线程处于等待阻塞状态,直到另一个线程调用notify()或notifyAll()通知;得到通知后线程进入锁定池等待锁定。
Condition类
类方法 | 描述 |
---|---|
acquire() | 线程锁 |
release() | 释放锁 |
wait(timeout) | 线程挂起,直到收到一个notify通知或者超时(可选的,浮点数,单位是秒s)才会被唤醒继续运行。wait()必须在已获得Lock前提下才能调用,否则会触发RuntimeError |
notify(n=1) | 通知其他线程,那些挂起的线程接到这个通知之后会开始运行,默认是通知一个正等待该condition的线程,最多则唤醒n个等待的线程。notify()必须在已获得Lock前提下才能调用,否则会触发RuntimeError。notify()不会主动释放Lock |
notifyAll() | 如果wait状态线程比较多,notifyAll的作用就是通知所有线程 |
# -*- coding:utf-8 -*-
import threading
import random
from queue import Queue
con = threading.Condition()
q = Queue(1)
def thread_0():
con.acquire()
for i in range(10):
if q.full:
val = q.get()
print("Get thread_0 id:", i, "value:", val)
con.notify()
con.wait(1)
con.release()
def thread_1():
con.acquire()
for j in range(10):
a = random.randint(0, 9)
if q.empty():
q.put(a)
print("Put thread_1 id:", j, "value:", a)
con.notify()
con.wait(1)
con.release()
if __name__ == "__main__":
thread = []
thread_name = [thread_1, thread_0]
for each in thread_name:
x = threading.Thread(target=each)
thread.append(x)
for each in range(len(thread)):
thread[each].start()
for each in range(len(thread)):
thread[each].join()
程序的输出如下:
Put thread_1 id: 0 value: 4
Get thread_0 id: 0 value: 4
Put thread_1 id: 1 value: 3
Get thread_0 id: 1 value: 3
Put thread_1 id: 2 value: 1
Get thread_0 id: 2 value: 1
Put thread_1 id: 3 value: 4
Get thread_0 id: 3 value: 4
Put thread_1 id: 4 value: 2
Get thread_0 id: 4 value: 2
Put thread_1 id: 5 value: 2
Get thread_0 id: 5 value: 2
Put thread_1 id: 6 value: 3
Get thread_0 id: 6 value: 3
Put thread_1 id: 7 value: 8
Get thread_0 id: 7 value: 8
Put thread_1 id: 8 value: 8
Get thread_0 id: 8 value: 8
Put thread_1 id: 9 value: 7
Get thread_0 id: 9 value: 7
结语
如果您有修改意见或问题,欢迎留言或者通过邮箱和我联系。
手打很辛苦,如果我的文章对您有帮助,转载请注明出处。