欢迎光临散文网 会员登陆 & 注册

子线程中event_loop的问题

2023-02-25 19:01 作者:韭菜怎么卖  | 我要投稿

    这个问题很有意思,就是子线程为什么不能调用get_event_loop。

    如果看过get_event_loop源码或者我之前的文章,你会知道get_event_loop本质就是:存在loop就get_running_loop,不存在就new_event_loop。但是在子线程中调用该方法会直接报错。这篇文章就是讲这个事情。

    需要明白的是:

    1、event_loop是一个对象。

    1、一个线程只能有一个在运行的event_loop,虽然你可以通过new_event_loop显式创建很多个。

    2、具体使用哪个event_loop,由set_event_loop显式控制,但极其不推荐在线程内创立多个loop切换着跑。

    3、既然一个线程只能有一个在跑的event_loop,event_loop又是个对象,那么多个线程可以共用同一个event_loop吗?可以,这是run_in_executor实现的原理。注意这里是可以,不是必须,也就是我们大可以用不同的线程跑不同的event_loop。

    4、子线程创立时,会默认继承主线程正在跑的event_loop吗?不会,因为event_loop是对象,触发你显式地传参然后set_event_loop,否则子线程创立之初就是没有event_loop在running的。

    5、子线程可以通过get_event_loop获取到主线程的event_loop吗?不可以,有一个小测试:

    get_running_loop会直接报错,说明子线程和主线程已经隔离。

    关于第四点的测试:

    这个测试有点复杂,但是可以得出的结论是:

    1、子线程确实可以和主线程共用一个loop,它们是同一个id,并且确实正在跑。

    2、子线程的loop由于正在跑,所以任何显式地run它,都会报错:already running。

    3、子线程提交任务create_task/ensure_future不会有任何反应。

    4、子线程想向主线程提交任务,使用方法run_coroutine_threadsafe,主线程会执行它并打印出来。

    5、以上写法可以理解为to_thread或者run_in_executor的一种实现。

    这就很匪夷所思:既然子线程有了event_loop,我们已经set它了,那么确实可以get到(可以在上面的代码,子线程中在set后主动get一下,你会发现是不会报错的),它也是在running的,为什么它提交了任务不跑呢?

    这一切都到get_event_loop里面来看:

    这一层没啥好说的,就是如果没有current_loop(就是running_loop),那么就调用policy创建一个loop。

    顺着这个policy一层一层往下翻,直到看到这么个东西:

    它在这里明确说明了,一个event_loop是attach到主线程上的。也就是说,子线程共用主线程的event_loop,但这个event_loop只会被attach到主线程上,子线程跳过这个操作。

    我们看它之下的set_event_loop:

    super里面是把loop赋值给一个_local_loop私有属性。这里明确写了,只有当存在watcher且是主线程时,set工作才会把event_loop和watcher绑定。

    我们再看基类中的get_event_loop:

    get_event_loop会创建loop的原理是第一个if,里面明确说明了,只有当前线程是主线程时,才会做这个操作。

    这下解释清楚了为什么子线程里面不能使用get_event_loop,但是没说清楚watcher到底是什么,它是怎样阻止create_task工作的,它为什么要这样设计,event_loop的底层到底是什么样的。

    create_task底层是调用Task这个pyi构造一个对象,这就超出我的能力范围了,本文就此烂尾。至少知道了该怎么写。原理什么的,我要是全都懂了,我还用它干啥?

    

子线程中event_loop的问题的评论 (共 条)

分享到微博请遵守国家法律