Python - 并发模型

发布时间 2023-05-08 21:55:01作者: chuangzhou
import itertools
import time
from threading import Thread, Event


def spin(msg: str, done: Event) -> None:
    for char in itertools.cycle(r'\|/-'): # 1
        status = f'\r{char}{msg}' # 2
        print(status, end='', flush=True)
        if done.wait(.1): # 3
            break # 4
    blanks = ' ' * len(status)
    print(f'\r{blanks}\r', end='') #5
    
def slow() -> int:
    time.sleep(3) # 7
    return 42

'''
1. 这是一个无限循环,因为itertools.cycle 一次产出一个字符,一直反复迭代字符串
2. 用文本实现动画的技巧: 使用ASCCI 回车符('\r') 把光标移动到行头
3. 如果其他线程设置了set(),则Event.wait(timeout=None) 方法返回True
   如果其他线程未设置set(), 则经过timeout 时间后返回False,这里把暂停时间设置为0.1秒,作用是把动画的
   帧率设为10fps。如果想指针旋转的快些,可以把值设置的小一些
4. 退出无限循环
5. 显示空格,并把光标移到开头,清空状态行
6.slow() 由主线程调用。假设有一个API通过网络发送,速度很慢。调用sleep 阻塞主线程,但是GIL已被释放,因此指针还能继续转动
'''

def supervisor() -> int:
    done = Event()
    spinner = Thread(target=spin, args=('thinking!', done))
    print(f'spinner object:{spinner}')
    spinner.start()
    result = slow()
    done.set()
    spinner.join()
    return result


def main() -> None:
    result = supervisor()
    print(f'AnswerL: {result}')


if __name__ == '__main__':
    main()


运行中:

运行结束:

通过这个实例,要了解最重要的一点:

调用 time.sleep()阻塞所在的线程,但是释放GIL,其他Pyhton线程可以继续运行
在Python中,协调线程的信号机制,使用threading.Event 类最简单。Event 实例有一个内部布尔标志,开始时为False,
调用Event.set() 可把这个标志设为True。这个标志为False时,在一个线程中调用Event.wait(),该线程将被阻塞,直到另一个线程
调用Event.set(),致使Event.wait()返回True。使用Event.wait(s) 设置一个暂停时间(单位秒),经过这段时间后,Event.wait(s)
调用返回False,如果一个线程调用Event.set(),则立即返回True