python 多线程的使用笔记
1、多线程的基本用法
(1)简单任务多线程的开启方式
from threading import Thread
import time
def target(name, n):
for i in range(0, n, 2):
print(f'{name}_{i}')
time.sleep(1)
t1 = Thread(target=target, args=('a', 16, ))
t1.start()
for i in range(1, 11, 2):
print(f'主线程_{i}')
time.sleep(1)
(2)自定义复杂任务多线程的开启方式,适合复杂逻辑
from threading import Thread
import time
class myThread(Thread):
def __init__(self, name, n):
super(myThread, self).__init__()
self.name = name
self.n = n
def run(self):
for i in range(0, self.n, 2):
print(f'{self.name}_{i}')
time.sleep(1)
t1 = myThread('a', 16, )
t1.start()
for i in range(1, 11, 2):
print(f'主线程_{i}')
time.sleep(1)
2、关于守护线程和 join
正常情况下主线程和子线程可以相互没有干扰,独立运行完后程序结束
当我们需要主线程结束后子线程也随着结束,可以将子线程设置为守护线程后,这样主线程结束后,即便子线程没有运行完,子线程也会随着主线程结束
这里注意的是,子线程设置守护线程一定要在子线程开始之前
当我们需要子线程运行完主线程再接着往下运行时,子线程可以调用 join()
方法, 主线程就会等待,直到子线程终止或者超时才会继续往下执行
这里注意的是,子线程 join()
一定要在子线程开始之后
jion()
里面可以传入 timeout
参数,超时后主线程会继续往下运行
下面是 setDaemo()
和 join()
的例子 可以去掉注释看看有什么不同
from threading import Thread
import time
def target(name, n):
for i in range(0, n, 2):
print(f'{name}_{i}')
time.sleep(1)
t1 = Thread(target=target, args=('a', 16,))
# t1.setDaemon(True)
t1.start()
# t1.join()
for i in range(1, 11, 2):
print(f'主线程_{i}')
time.sleep(1)
3、关于信号量 Semaphore()
和 BoundedSemaphore()
我们可以一次开启很多线程,但是我们想对同时运行的线程控制在一定数量内,开启的线程大于同时运行的线程时,其他的线程就处于阻塞状态,直到有线程运行结束,阻塞的线程才能开始运行,这时我们就用到 Semaphore()
,
信号量基于内部计数器,初始化 Semaphore()
时会给计数器一个值,每调用一次acquire()
,计数器减1,每调用一次release()
, 计数器加1,当计数器为0时,acquire()
调用被阻塞
from threading import Thread, Semaphore
import time
sema = Semaphore(3)
def target(name, n):
sema.acquire()
for i in range(0, n, 2):
print(f'{name}_{i}')
time.sleep(1)
sema.release()
sema = Semaphore(3)
for s in 'abcde':
Thread(target=target, args=(s, 6,)).start()
Semaphore()
支持上下文,对于上面代码,也可以这样写
from threading import Thread, Semaphore
import time
sema = Semaphore(3)
def target(name, n):
with sema:
for i in range(0, n, 2):
print(f'{name}_{i}')
time.sleep(1)
sema = Semaphore(3)
for s in 'abcde':
Thread(target=target, args=(s, 6,)).start()
BoundedSemaphore
继承自 Semaphore
功能和 Semaphore
基本一样,只不过 BoundedSemaphore
有限制,release()
时会判断当前信号量的值,如果当前值大于等于初始值,就会抛出错误,而Semaphore
并不会抛出异常
4 锁 Lock
和 RLock
如果运行上面代码,我们会发现,多个子线程之间可能打印在一行上,比较混乱,这是由于线程之间随机调度,同时输出。如果是同时在修改一个变量。或者一个文件,就有可能造成数据错误,这时互斥锁Lock
就派上用场了。Lock
可以用于锁定资源,Lock
有两个基本方法,acquire()
和 release()
,调用acquire()
状态变为锁定并立即返回,当状态是锁定时, acquire()
将阻塞至其他线程调用 release()
将其改为非锁定状态,然后 acquire()
调用重置其为锁定状态并返回。锁支持上下文管理协议,即支持with语
句
from threading import Thread, Lock
import time
lock = Lock()
def target(name, n):
for i in range(0, n, 2):
lock.acquire()
print(f'{name}_{i}')
lock.release()
time.sleep(1)
for s in 'abcde':
Thread(target=target, args=(s, 6,)).start()
用with
语句代替
from threading import Thread, Lock
import time
lock = Lock()
def target(name, n):
for i in range(0, n, 2):
with lock:
print(f'{name}_{i}')
time.sleep(1)
for s in 'abcde':
Thread(target=target, args=(s, 6,)).start()
如果尝试释放一个非锁定的锁,则会引发 RuntimeError
异常
RLock
被称为重入锁,若要锁定锁,线程调用其 acquire()
方法;一旦线程拥有了锁,方法将返回。若要解锁,线程调用 release()
方法。 acquire()/release()
对可以嵌套,重入锁必须由获取它的线程释放。一旦线程获得了重入锁,同一个线程再次获取它将不阻塞。只有最终release()
(最外面一对的 release()
) 将锁解开,才能让其他线程继续处理 acquire()
阻塞,线程必须在每次获取它时释放一次。
Lock
和 RLock
用法大体相同,区别在于Lock
在锁定时不属于特定线程,也就是说,Lock
可以在一个线程中上锁,在另一个线程中解锁。而对于RLock
来说,只有当前线程才能释放本线程上的锁,即解铃还须系铃人。
5 Event
事件
Event
内部定义了一个标志位flag
,初始的时候为False
,当Flag
的值为False
,那么event.wait()
就会阻塞,当flag
值为True
,那么event.wait()
便不再阻塞Event
主要提供以下方法
set()
来将其设置为True
;
clear()
将其重新设置为False
;
is_set()
来检查标志位的状态;
wait(timeout=None)
,会一直监听flag
,如果flag
为False
就一直处于阻塞状态
from threading import Thread, Event
import time
class myThread(Thread):
def __init__(self, name, n):
super(myThread, self).__init__()
self.name = name
self.n = n
self.e = Event()
def pause(self): # 暂停
self.e.clear()
def play(self): # 继续
self.e.set()
def run(self):
for i in range(0, self.n, 2):
self.e.wait()
print(f'{self.name}_{i}')
time.sleep(1)
t1 = myThread('a', 16, )
t1.play()
t1.start()
for i in range(1, 11, 2):
print(f'主线程_{i}')
time.sleep(1)
if i == 3:
t1.pause() # 子线程暂停
if i == 7:
t1.play() # 子线程继续
特别感谢下面两篇文章的作者,供大家做参考学习
python多线程详解(超详细)
python threading模块的Lock和RLock区别