Python解決多線程運(yùn)行異步代碼報(bào)錯(cuò)"There?is?no?current?event?loop"
1. 引言
在Python開(kāi)發(fā)中,我們經(jīng)常需要同時(shí)處理高并發(fā)網(wǎng)絡(luò)請(qǐng)求和CPU密集型任務(wù)。這時(shí),開(kāi)發(fā)者可能會(huì)選擇:
- 多線程(ThreadPoolExecutor)處理阻塞IO任務(wù)
- 異步IO(asyncio + aiohttp)優(yōu)化高并發(fā)網(wǎng)絡(luò)請(qǐng)求
然而,當(dāng)嘗試在多線程環(huán)境中運(yùn)行異步代碼時(shí),可能會(huì)遇到錯(cuò)誤:
ERROR - There is no current event loop in thread 'Thread-4'.
本文將分析該問(wèn)題的原因,并提供3種解決方案,包括:
- 純同步方案(requests)
- 異步+多線程方案(aiohttp + asyncio.run_coroutine_threadsafe)
- 多進(jìn)程替代方案(ProcessPoolExecutor)
最后,我們還會(huì)給出Java的等效實(shí)現(xiàn)(基于CompletableFuture和HttpClient)。
2. 問(wèn)題背景
2.1 錯(cuò)誤復(fù)現(xiàn)
以下代碼在多線程中調(diào)用異步函數(shù)時(shí)崩潰:
from concurrent.futures import ThreadPoolExecutor
import asyncio
async def async_task():
await asyncio.sleep(1)
return "Done"
def run_in_thread():
# 直接調(diào)用會(huì)報(bào)錯(cuò):There is no current event loop in thread
result = async_task() # ? 錯(cuò)誤!
return result
with ThreadPoolExecutor() as executor:
future = executor.submit(run_in_thread)
print(future.result())
2.2 原因分析
asyncio 的事件循環(huán)是線程局部的,每個(gè)線程需要自己的事件循環(huán)。
主線程默認(rèn)有事件循環(huán),但子線程沒(méi)有。
直接在新線程中調(diào)用 await 會(huì)導(dǎo)致 RuntimeError。
3. 解決方案
3.1 方案1:純同步實(shí)現(xiàn)(推薦)
如果不需要高性能異步IO,直接改用同步請(qǐng)求庫(kù)(如requests):
import requests
def sf_express_order_count_sync(consigneePhone, cookie, createTimeStart, createTimeEnd):
"""同步版:使用requests發(fā)送HTTP請(qǐng)求"""
url = 'https://sd.sf-express.com/api/merge/order/count'
response = requests.post(url, headers=headers, json=payload)
return response.json()
優(yōu)點(diǎn):
- 代碼簡(jiǎn)單,無(wú)需處理事件循環(huán)
- 兼容所有Python版本
缺點(diǎn):
性能較低(每個(gè)請(qǐng)求阻塞線程)
3.2 方案2:異步+多線程混合
如果必須用異步IO(如aiohttp),需為每個(gè)線程創(chuàng)建事件循環(huán):
import aiohttp
async def sf_express_order_count_async(consigneePhone, cookie, createTimeStart, createTimeEnd):
"""異步版:使用aiohttp"""
async with aiohttp.ClientSession() as session:
async with session.post(url, headers=headers, json=payload) as resp:
return await resp.json()
def run_async_in_thread(async_func, *args):
"""在子線程中運(yùn)行異步函數(shù)"""
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
try:
return loop.run_until_complete(async_func(*args))
finally:
loop.close()
???????# 在線程池中調(diào)用
with ThreadPoolExecutor() as executor:
future = executor.submit(
run_async_in_thread,
sf_express_order_count_async,
"13112345678",
"cookie123",
1630000000
)
print(future.result())優(yōu)點(diǎn):
- 高性能(異步IO + 多線程)
- 適用于高并發(fā)場(chǎng)景
缺點(diǎn):
- 代碼復(fù)雜度高
- 需要手動(dòng)管理事件循環(huán)
3.3 方案3:改用多進(jìn)程
如果異步+多線程仍不滿足需求,可用ProcessPoolExecutor替代:
from concurrent.futures import ProcessPoolExecutor
def check_phones_with_processes(phone_numbers):
"""使用進(jìn)程池規(guī)避GIL和事件循環(huán)問(wèn)題"""
with ProcessPoolExecutor() as executor:
futures = [executor.submit(has_orders, phone) for phone in phone_numbers]
for future in as_completed(futures):
if future.result():
return future.result()
優(yōu)點(diǎn):
- 繞過(guò)GIL限制
- 每個(gè)進(jìn)程有獨(dú)立的事件循環(huán)
缺點(diǎn):
- 進(jìn)程啟動(dòng)開(kāi)銷大
- 進(jìn)程間通信復(fù)雜
4. Java等效實(shí)現(xiàn)
在Java中,可以使用CompletableFuture和HttpClient實(shí)現(xiàn)類似功能:
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.concurrent.CompletableFuture;
public class SfExpressChecker {
private static final HttpClient httpClient = HttpClient.newHttpClient();
public static CompletableFuture<Boolean> hasOrdersAsync(String phone, String cookie) {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://sd.sf-express.com/api/merge/order/count"))
.header("Content-Type", "application/json")
.header("token", cookie)
.POST(HttpRequest.BodyPublishers.ofString(
String.format("{\"consigneePhone\":\"%s\"}", phone)))
.build();
return httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.thenApply(response -> {
JsonObject json = JsonParser.parseString(response.body()).getAsJsonObject();
return json.get("result").getAsJsonObject().get("total").getAsInt() > 0;
});
}
public static void main(String[] args) {
CompletableFuture<Boolean> future = hasOrdersAsync("13112345678", "cookie123");
future.thenAccept(hasOrders -> System.out.println("Has orders: " + hasOrders));
future.join(); // 阻塞等待結(jié)果
}
}
關(guān)鍵點(diǎn):
- Java的HttpClient原生支持異步
- CompletableFuture簡(jiǎn)化異步編程
- 無(wú)需手動(dòng)管理事件循環(huán)
5. 總結(jié)
| 方案 | 適用場(chǎng)景 | 優(yōu)點(diǎn) | 缺點(diǎn) |
|---|---|---|---|
| 純同步(requests) | 低并發(fā)、簡(jiǎn)單場(chǎng)景 | 代碼簡(jiǎn)單 | 性能差 |
| 異步+多線程 | 高并發(fā)網(wǎng)絡(luò)請(qǐng)求 | 高性能 | 需管理事件循環(huán) |
| 多進(jìn)程 | CPU密集型+高IO混合任務(wù) | 繞過(guò)GIL | 進(jìn)程開(kāi)銷大 |
最終建議:
- 優(yōu)先使用同步代碼(除非性能瓶頸明確)
- 異步+多線程適合高并發(fā)HTTP請(qǐng)求
- Java的異步方案更優(yōu)雅(推薦
CompletableFuture)
通過(guò)合理選擇方案,可以避免There is no current event loop錯(cuò)誤,并構(gòu)建高性能的并發(fā)應(yīng)用。
到此這篇關(guān)于Python解決多線程運(yùn)行異步代碼報(bào)錯(cuò)"There is no current event loop"的文章就介紹到這了,更多相關(guān)Python解決多線程運(yùn)行異步報(bào)錯(cuò)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Python多線程與異步處理在HTTP請(qǐng)求中的應(yīng)用方式
- python異步爬蟲(chóng)之多線程
- Python異步爬蟲(chóng)多線程與線程池示例詳解
- python并發(fā)編程之多進(jìn)程、多線程、異步和協(xié)程詳解
- Python2到Python3的遷移過(guò)程中報(bào)錯(cuò)AttributeError: ‘str‘ object has no attribute ‘decode‘問(wèn)題的解決方案大全
- Python中解決schedule模塊安裝與使用問(wèn)題的完整指南
- Python解決“ImportError:?Couldn‘t?import?Django”問(wèn)題全攻略
- 如何解決Python:報(bào)錯(cuò)[Errno 2]No such file or directory問(wèn)題
相關(guān)文章
利用Pytorch實(shí)現(xiàn)ResNet網(wǎng)絡(luò)構(gòu)建及模型訓(xùn)練
這篇文章主要為大家介紹了利用Pytorch實(shí)現(xiàn)ResNet網(wǎng)絡(luò)構(gòu)建及模型訓(xùn)練詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-04-04
Pycharm設(shè)置utf-8自動(dòng)顯示方法
今天小編就為大家分享一篇Pycharm設(shè)置utf-8自動(dòng)顯示方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-01-01
Python實(shí)現(xiàn)文件從一個(gè)文件夾復(fù)制到另一個(gè)文件夾的步驟詳解
在Python編程中,文件復(fù)制是一個(gè)常見(jiàn)的操作,它允許我們將一個(gè)文件的內(nèi)容復(fù)制到另一個(gè)文件中,文件復(fù)制操作在許多實(shí)際應(yīng)用中都非常有用,比如備份文件、數(shù)據(jù)分析和處理等,在本文中,我們將詳細(xì)如何使用Python將文件從一個(gè)文件夾復(fù)制到另一個(gè)文件夾2025-09-09
Python使用watchdog實(shí)時(shí)監(jiān)測(cè)文件或文件夾的更改
這篇文章主要介紹了如何使用Python的watchdog庫(kù)來(lái)實(shí)時(shí)監(jiān)控文件和文件夾的創(chuàng)建、修改、刪除操作,實(shí)現(xiàn)熱重載機(jī)制和其他應(yīng)用場(chǎng)景,需要的朋友可以參考下2026-03-03
Python實(shí)現(xiàn)無(wú)損放大圖片的示例代碼
這篇文章主要為大家詳細(xì)介紹了如何利用Python語(yǔ)言實(shí)現(xiàn)一個(gè)簡(jiǎn)單的無(wú)損放大圖片小程序,可以支持將JPG/PNG圖片無(wú)損放大上萬(wàn)像素,感興趣的可以了解一下2022-08-08
python用opencv批量截取圖像指定區(qū)域的方法
今天小編就為大家分享一篇python用opencv批量截取圖像指定區(qū)域的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-01-01
python裝飾器實(shí)現(xiàn)對(duì)異常代碼出現(xiàn)進(jìn)行自動(dòng)監(jiān)控的實(shí)現(xiàn)方法
這篇文章主要介紹了python裝飾器實(shí)現(xiàn)對(duì)異常代碼出現(xiàn)進(jìn)行自動(dòng)監(jiān)控的實(shí)現(xiàn)方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-09-09
Python圖像處理庫(kù)PIL詳細(xì)使用說(shuō)明
Pillow是Python中較為基礎(chǔ)的圖像處理庫(kù),主要用于圖像的基本處理,比如裁剪圖像、調(diào)整圖像大小和圖像顏色處理等,需要的朋友可以參考下2022-04-04

