如何在django視圖中使用asyncio(協程)和ThreadPoolExecutor(多線程)


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在主線程中調用一次,它將自動建立一個循環對象並將其設置爲默認值,可是若是你在一個子線程中再次調用它,你會獲得這個錯誤。相反,您須要在線程啓動時顯式建立/設置事件循環:html

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/75046718python

  https://www.cnblogs.com/zhaof/p/8490045.htmlweb

  https://stackoverflow.com/questions/41594266/asyncio-with-djangodjango

相關文章
相關標籤/搜索