由浅入深
1.创建子线程
对于threading模块有两种创建子线程的方法
1.1继承threading.Thread
类,并重写run()
- 继承后重写
.start()
开始子线程,使之进入run()
import threading
class MyThread(threading.Thread):
def __init__(self, id):
threading.Thread.__init__(self)
self.id = id
def run(self):
print('your thread is running')
t1 = MyThread(999)
t1.start()
1.2 直接使用threading.Thread
类
.start()
开始子线程,使之进入run()
import threading
def test_run():
print('your thread is running')
if __name__ == "__main__":
t = threading.Thread(target=test_run(), args=())
t.start()
target
就是线程要执行的代码段,和上面方法的run()
是一样的,args
是传入到target
函数的参数
2.控制子线程和主线程
主线程:就是生成子线程的线程,简单来说就是main函数
以下所有的print
混乱(即仅针对本例的print两两不匹配)都是输出缓冲区的原因,在terminal端加-u
禁用缓冲区即可
python -u test.py
2.1 最简单的多线程示例
import threading
import time
def test_run(arg):
time.sleep(1)
print('sub thread start!the thread name is:%s\r' % threading.currentThread().getName())
print('the arg is:%s\r' %arg)
time.sleep(1)
for i in range(4):
t = threading.Thread(target=test_run,args=(i,))
t.start()
print('main_thread end!')
结果:
main_thread end!
sub thread start!the thread name is:Thread-3
sub thread start!the thread name is:Thread-2
the arg is:0
the arg is:1
sub thread start!the thread name is:Thread-4
the arg is:3
可能不同的机器不同时间跑的都不一样,多线程嘛,
但是都是主线程先执行完 -> 再在最后等待子线程结束 -> 程序结束。
---------------------------------
t.setDaemon()
2.2 主线程优先
当代码部分被替换为如下
t = threading.Thread(target=test_run,args=(i,))
t.daemon(True)
# t = threading.Thread(target=test_run,args=(i,), daemon=True)
结果:
main_thread end!
主线程执行完直接结束程序,无视子线程的状态,也终止
---------------------------------
t.join()
2.3 主线程在join处等待子线程完成后继续
import threading
import time
def test_run(arg):
time.sleep(1)
print('sub thread start!the thread name is:%s\r' % threading.currentThread().getName())
print('the arg is:%s\r' %arg)
time.sleep(1)
for i in range(4):
t = threading.Thread(target=test_run,args=(i,), daemon=True)
t.start()
t.join()
print('main_thread end!')
结果:
sub thread start!the thread name is:Thread-1
the arg is:0
sub thread start!the thread name is:Thread-2
the arg is:1
sub thread start!the thread name is:Thread-3
the arg is:2
sub thread start!the thread name is:Thread-4
the arg is:3
main_thread end!
程序变成了顺序执行,原因就是主线程在每次子线程调用.join()
时,都被阻塞,不能继续开启线程,程序就失去了多线程的意义。
正确的code:
import threading
import time
def test_run(arg):
time.sleep(1)
print('sub thread start!the thread name is:%s\r' % threading.currentThread().getName())
print('the arg is:%s\r' %arg)
time.sleep(1)
threadlist = []
for i in range(4):
t = threading.Thread(target=test_run,args=(i,), daemon=True)
threadlist.append(t)
for t in threadlist:
t.start()
for t in threadlist:
t.join()
结果:
sub thread start!the thread name is:Thread-1
sub thread start!the thread name is:Thread-3
the arg is:1
the arg is:0
sub thread start!the thread name is:Thread-4
the arg is:2
the arg is:3
main_thread end!
做到了子线程先执行完,在完成主线程,并且不是顺序执行。
3.利用threading.Event()
管理线程
3.1 threading.Event()
内部维护一个信号量_flag
.wait()
调用该函数的线程并且信号量此时正好为False
,则被阻塞(包括主线程);当设置了timeout
,时间到会停止阻塞,继续执行,不改变信号量
.set()
时将信号量设为True
,因调用wait
函数阻塞的所有线程将被唤醒
.clear()
将信号量设为False
.isSet()
检测信号量是否为True
,是则本省返回True
,否则返回False
上文的函数都可以自己试一试,很好玩的~
基本code:
import threading
import time
def action(arg, event):
while not event.isSet():
print('Thread %s is ready' % arg)
time.sleep(1)
event.wait()
while event.isSet():
print('Thread %s is running' % arg)
time.sleep(1)
event = threading.Event() # 初始信号量为False
for i in range(3):
t =threading.Thread(target=action,args=(i,event))
t.start()
time.sleep(2)
print('-----set-----')
event.set() # set后 信号量置True wait里的子线程都被唤醒
time.sleep(2)
print('-----clear----')
event.clear() # 信号量置False 子线程跳出第二个循环
print('main_thread end!')
输出:
Thread 0 is ready
Thread 1 is ready
Thread 2 is ready
Thread 0 is ready
Thread 1 is ready
Thread 2 is ready
-----set-----
Thread 0 is running
Thread 1 is running
Thread 2 is running
Thread 1 is running
Thread 0 is running
Thread 2 is running
-----clear----
main_thread end!
在正式讲threading.Event()
实现的互斥前先讲别的方法
3.2 threading.RLock()
控制互斥
import threading
import time
total=5
def sale():
global total
time.sleep(.5)
print(total)
time.sleep(.5)
total-=1
if __name__ == '__main__':
threads=[]
for i in range(5):
t=threading.Thread(target=sale,args=())
threads.append(t)
for t in threads:
t.start()
结果:
5
5
5
5
5
我们是想获得5 4 3 2 1
的,但很显然都同一时刻输出total原始值。
import threading
import time
total=5
lock=threading.RLock() # 创建锁
def sale():
global total
lock.acquire()
time.sleep(.5)
print(total)
time.sleep(.5)
total-=1
lock.release()
if __name__ == '__main__':
threads=[]
for i in range(5):
t=threading.Thread(target=sale,args=())
threads.append(t)
for t in threads:
t.start()
结果自然正确,.acquire()
上锁 .release()
解锁
3.2 threading.Event()
内部维护的互斥
源码中有:
def __init__(self):
self._cond = Condition(Lock())
self._flag = False
Condition是什么??
Condition是一个类,类初始化时,传入了一个可重入锁Lock()或默认的RLock()锁。源码可以看出来
所以threading.Event()
可以通过.wait()
.set()
.clear()
等完成线程的协同。
多线程程序编写时,灵活运用 lock,event控制,程序会很灵活
一个生产者消费者的示例,可供学习
https://blog.csdn.net/u013346751/article/details/78519708