(python)threading模块简单讲解

由浅入深

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值