子線程中event_loop的問題
????這個問題很有意思,就是子線程為什么不能調(diào)用get_event_loop。
????如果看過get_event_loop源碼或者我之前的文章,你會知道get_event_loop本質(zhì)就是:存在loop就get_running_loop,不存在就new_event_loop。但是在子線程中調(diào)用該方法會直接報錯。這篇文章就是講這個事情。
????需要明白的是:
????1、event_loop是一個對象。
????1、一個線程只能有一個在運行的event_loop,雖然你可以通過new_event_loop顯式創(chuàng)建很多個。
????2、具體使用哪個event_loop,由set_event_loop顯式控制,但極其不推薦在線程內(nèi)創(chuàng)立多個loop切換著跑。
????3、既然一個線程只能有一個在跑的event_loop,event_loop又是個對象,那么多個線程可以共用同一個event_loop嗎?可以,這是run_in_executor實現(xiàn)的原理。注意這里是可以,不是必須,也就是我們大可以用不同的線程跑不同的event_loop。
????4、子線程創(chuàng)立時,會默認(rèn)繼承主線程正在跑的event_loop嗎?不會,因為event_loop是對象,觸發(fā)你顯式地傳參然后set_event_loop,否則子線程創(chuàng)立之初就是沒有event_loop在running的。
????5、子線程可以通過get_event_loop獲取到主線程的event_loop嗎?不可以,有一個小測試:
????get_running_loop會直接報錯,說明子線程和主線程已經(jīng)隔離。
????關(guān)于第四點的測試:
????這個測試有點復(fù)雜,但是可以得出的結(jié)論是:
????1、子線程確實可以和主線程共用一個loop,它們是同一個id,并且確實正在跑。
????2、子線程的loop由于正在跑,所以任何顯式地run它,都會報錯:already running。
????3、子線程提交任務(wù)create_task/ensure_future不會有任何反應(yīng)。
????4、子線程想向主線程提交任務(wù),使用方法run_coroutine_threadsafe,主線程會執(zhí)行它并打印出來。
????5、以上寫法可以理解為to_thread或者run_in_executor的一種實現(xiàn)。
????這就很匪夷所思:既然子線程有了event_loop,我們已經(jīng)set它了,那么確實可以get到(可以在上面的代碼,子線程中在set后主動get一下,你會發(fā)現(xiàn)是不會報錯的),它也是在running的,為什么它提交了任務(wù)不跑呢?
????這一切都到get_event_loop里面來看:
????這一層沒啥好說的,就是如果沒有current_loop(就是running_loop),那么就調(diào)用policy創(chuàng)建一個loop。
????順著這個policy一層一層往下翻,直到看到這么個東西:
????它在這里明確說明了,一個event_loop是attach到主線程上的。也就是說,子線程共用主線程的event_loop,但這個event_loop只會被attach到主線程上,子線程跳過這個操作。
????我們看它之下的set_event_loop:
????super里面是把loop賦值給一個_local_loop私有屬性。這里明確寫了,只有當(dāng)存在watcher且是主線程時,set工作才會把event_loop和watcher綁定。
????我們再看基類中的get_event_loop:
????get_event_loop會創(chuàng)建loop的原理是第一個if,里面明確說明了,只有當(dāng)前線程是主線程時,才會做這個操作。
????這下解釋清楚了為什么子線程里面不能使用get_event_loop,但是沒說清楚watcher到底是什么,它是怎樣阻止create_task工作的,它為什么要這樣設(shè)計,event_loop的底層到底是什么樣的。
????create_task底層是調(diào)用Task這個pyi構(gòu)造一個對象,這就超出我的能力范圍了,本文就此爛尾。至少知道了該怎么寫。原理什么的,我要是全都懂了,我還用它干啥?
????