天天看點

Python - 多任務:協程

Python - 多任務:協程

        • 并行 and 并發
        • 協程
        • yield 實作
        • greenlet 實作
        • gevent 實作
  • 并行 and 并發

    在多任務中,存在兩個概念:并發與并行。

    其中并發是假的多任務:在一個時間段中有多個程式都處于運作狀态,且這幾個程式是在同一個處理機上運作的,但任一個時刻點上實際隻有一個程式在處理機上運作。在宏觀上是同時進行,但微觀上仍是順序執行,是以稱之為假的多任務。

    而并行則是真的多任務:多個程式同時在多個處理機上運作,宏觀上同時進行,在微觀上仍然是同時進行的,是以是真的多任務

    對于之前講過的多線程,根據具體運作環境的不同,它所屬情況也不同。例如在 Java 中多線程就是真的多任務,是并行計算,而在 Python 中,由于全局解釋器鎖的存在(GIL,Global Interpreter Lock),使得在同一程序内任何時刻僅有一個線程在執行,是以運作中是假的多任務。

  • 協程

    Python 中的協程仍然是假的多任務,本質是調用多個生成器的

    __next__()

    方法,達到多個生成器交替運作的效果。

    協程運作于單線程中,任務排程(切換執行任務)都相當于調用函數,是以耗費的資源非常的少。(多程序最耗費資源,多線程次之,協程最少)

    下面看一下協程的實作方式。

  • yield 實作

    實際上通過調用

    __next__()

    實作協程,使得多個函數交替進行。
    def task_1():
        while True:
            print("1")
            yield
    
    
    def task_2():
        while True:
            print("2")
            yield
    
    
    def main():
        t1 = task_1()
        t2 = task_2()
        while True:
            next(t1)
            next(t2)
    
    main()
               
  • greenlet 實作

    import greenlet
    
    def task_1():
        while True:
            print("1")
            # 手動切換到 task_2
            g2.switch()
    
    
    def task_2():
        while True:
            print("2")
            # 手動切換到 task_1
            g1.switch()
    
    g1 = greenlet(task_1)
    g2 = greenlet(task_2)
               
  • gevent 實作

from gevent import monkey
	import gevent

    # 協程 gevent 在延時(或執行耗時操作、阻塞)的時候會自動切換任務
    # 協程就是利用 IO 等待時間執行其他任務
    # 方法内部使用 gevent.sleep() 進行延時操作,但手動添加 sleep 不現實,
    # 是以使用 monkey.patch_all() 自動将所有等待操作替換成 gevent.sleep()
	monkey.patch_all()

	def task_1():
	    while True:
	        print("1")
	        # 手動切換到 task_2
	        g2.switch()
	
	def task_2():
	    while True:
	        print("2")
	        # 手動切換到 task_1
	        g1.switch()

	g1 = gevent.spawn(task_1)
	g2 = gevent.spawn(task_2)
	
	# 或者 gevent.joinall([g1, g2])
    g1.join()
    g2.join()