Django視圖函數執行,不在主線程中,直接
loop = asyncio.new_event_loop() # 更不能loop = asyncio.get_event_loop()
會觸發
RuntimeError: There is no current event loop in thread
因為asyncio程序中的每個線程都有自己的事件循環,但它只會在主線程中為你自動創建一個事件循環。所以如果你asyncio.get_event_loop在主線程中調用一次,它將自動創建一個循環對象並將其設置為默認值,但是如果你在一個子線程中再次調用它,你會得到這個錯誤。相反,您需要在線程啟動時顯式創建/設置事件循環:
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
在Django單個視圖中使用asyncio實例代碼如下(有多個IO任務時)
from django.views import View
import asyncio
import time
from django.http import JsonResponse
class TestAsyncioView(View):
def get(self, request, *args, **kwargs):
"""
利用asyncio和async await關鍵字(python3.5之前使用yield)實現協程
"""
start_time = time.time()
loop = asyncio.new_event_loop() # 或 loop = asyncio.SelectorEventLoop()
asyncio.set_event_loop(loop)
self.loop = loop
try:
results = loop.run_until_complete(self.gather_tasks())
finally:
loop.close()
end_time = time.time()
return JsonResponse({'results': results, 'cost_time': (end_time - start_time)})
async def gather_tasks(self):
"""
也可以用回調函數處理results
task1 = self.loop.run_in_executor(None, self.io_task1, 2)
future1 = asyncio.ensure_future(task1)
future1.add_done_callback(callback)
def callback(self, future):
print("callback:",future.result())
"""
tasks = (
self.make_future(self.io_task1, 2),
self.make_future(self.io_task2, 2)
)
results = await asyncio.gather(*tasks)
return results
async def make_future(self, func, *args):
future = self.loop.run_in_executor(None, func, *args)
response = await future
return response
"""
# python3.5之前無async await寫法
import types
@types.coroutine
# @asyncio.coroutine # 這個也行
def make_future(self, func, *args):
future = self.loop.run_in_executor(None, func, *args)
response = yield from future
return response
"""
def io_task1(self, sleep_time):
time.sleep(sleep_time)
return 66
def io_task2(self, sleep_time):
time.sleep(sleep_time)
return 77
在Django單個視圖中使用ThreadPoolExecutor實例代碼如下(有多個IO任務時)
from django.views import View
import time
from concurrent.futures import ThreadPoolExecutor, as_completed
class TestThreadView(View):
def get(self, request, *args, **kargs):
start_time = time.time()
future_set = set()
tasks = (self.io_task1, self.io_task2)
with ThreadPoolExecutor(len(tasks)) as executor:
for task in tasks:
future = executor.submit(task, 2)
future_set.add(future)
for future in as_completed(future_set):
error = future.exception()
if error is not None:
raise error
results = self.get_results(future_set)
end_time = time.time()
return JsonResponse({'results': results, 'cost_time': (end_time - start_time)})
def get_results(self, future_set):
"""
處理io任務執行結果,也可以用future.add_done_callback(self.get_result)
def get(self, request, *args, **kargs):
start_time = time.time()
future_set = set()
tasks = (self.io_task1, self.io_task2)
with ThreadPoolExecutor(len(tasks)) as executor:
for task in tasks:
future = executor.submit(task, 2).add_done_callback(self.get_result)
future_set.add(future)
for future in as_completed(future_set):
error = future.exception()
print(dir(future))
if error is not None:
raise error
self.results = results = []
end_time = time.time()
return JsonResponse({'results': results, 'cost_time': (end_time - start_time)})
def get_result(self, future):
self.results.append(future.result())
"""
results = []
for future in future_set:
results.append(future.result())
return results
def io_task1(self, sleep_time):
time.sleep(sleep_time)
return 10
def io_task2(self, sleep_time):
time.sleep(sleep_time)
return 66
附tornado中不依賴異步庫實現異步非阻塞
from tornado.web import RequestHandler
from concurrent.futures import ThreadPoolExecutor
class NonBlockingHandler(RequestHandler):
"""
不依賴tornado的異步庫實現異步非阻塞
使用 gen.coroutine 裝飾器編寫異步函數,如果庫本身不支持異步,那么響應任然是阻塞的。
在 Tornado 中有個裝飾器能使用 ThreadPoolExecutor 來讓阻塞過程編程非阻塞,
其原理是在 Tornado 本身這個線程之外另外啟動一個線程來執行阻塞的程序,從而讓 Tornado 變得非阻塞
"""
executor = ThreadPoolExecutor(max_workers=2)
# executor默認需為這個名字,否則@run_on_executor(executor='_thread_pool')自定義名字,經測試max_workers也可以等於1
@coroutine # 使用@coroutine這個裝飾器加yield關鍵字,或者使用async加await關鍵字
def get(self, *args, **kwargs):
second = yield self.blocking_task(20)
self.write('noBlocking Request: {}'.format(second))
"""
async def get(self, *args, **kwargs):
second = await self.blocking_task(5)
self.write('noBlocking Request: {}'.format(second))
"""
@run_on_executor
def blocking_task(self, second):
"""
阻塞任務
"""
time.sleep(second)
return second
參考 https://blog.csdn.net/qq_34367804/article/details/75046718
https://www.cnblogs.com/zhaof/p/8490045.html
https://stackoverflow.com/questions/41594266/asyncio-with-django
