帶裝飾器的Python中的簡化多進程、多線程併發(裝飾併發-Python多線程、進程神器)

多線程、多進程、協程的基本原理概念、以及Python中的基本實現方法。請看筆者之前的寫的文章。
傳送門-Python中理解進程(Process),線程(Thread)和協程(Coroutines)的感悟

威斯康星大學麥迪遜分校的Alex Sherman和Peter Den Hartog編寫了一個新的有趣的Python多處理程序包裝,稱爲deco。

這是在Python中同時運行代碼的簡化方法。由於CPython全局解釋器鎖,運行Python中的併發代碼當前需要使用多個獨立的進程,並對它們的函數調用和參數進行序列化和管道化。

deco
論文paper: DECO: Polishing Python Parallel Programming
代碼: https://github.com/alex-sherman/deco
基本使用和安裝教程請看Readme.md

該庫基於稱爲Pydron的東西,2016年時有人稱這個項目是一項研究,當時並未沒有發佈任何代碼。

除了在函數上使用簡單的裝飾器之外,最大的不同在於deco,它真的很容易上手,並且對如何收集子流程調用的結果也有嚴格的限制。

在deco中,您傳入具有鍵索引(例如python )的可變對象dict。python list 也是可變的,但沒有索引。意思是,您可以獲取處理信息通過mylist.append()。

“但是,DECO確實對該程序施加了一個重要限制:所有突變都只能基於索引。”

先來看一個基本示例瞭解deco如何工作:

@concurrent   #Identify the concurrent function
def do_work(key):
  return some_calculations(...)

data = {}
@synchronized
def run():
  for key in data:
    data[key] = do_work(key)
  print data # data will be automatically synchronized here

程序員唯一干預就是插入兩個裝飾器@concurrent和@synchronized。在@concurrent你想一個函數來並行運行標識及@synchronized裝飾標記的功能,其中併發功能將被調用。

爲什麼說deco強大,真正的理由是這樣的:

我們可以修改併發函數中的參數,並且修改的內容同步回父進程。這不同於Python的multiprocess.pool,後者要求從併發函數中返回任何已更改的狀態,並丟棄對參數的修改。例如,您可以擁有一個經度範圍的字典,並調用一個@concurrent函數,該函數計算給定範圍內的平均溫度並更新該範圍的鍵。只要您在循環中調用該函數,並且每次調用該函數都更新字典中的唯一鍵,您的計算就將並行進行,並且在訪問父級中的數據之前,將自動同步各個過程的結果處理(@synchronized 裝飾者可以處理)。

如上面的示例所示,您可以將併發函數調用的結果分配給索引或鍵對象(列表和字典),並在並行調用完成後以及在父進程中訪問數據之前將它們同步到這些位置。再次,@synchronized裝飾器使之成爲可能,該裝飾器實際上重寫了父函數體內的分配,以允許它們同時發生並在以後進行同步。

實際使用多進程完整模板:

import deco
import time
import random
from collections import defaultdict


@deco.concurrent(processes=16) 
def process_lat_lon(lat, lon, data):
    """
    We add this for the concurrent function
    :param lat: 
    :param lon: 
    :param data: 
    :return: 
    """
    time.sleep(0.1)
    return data[lat + lon]


@deco.synchronized  
def process_data_set(data):
    """
    And we add this for the function which calls the concurrent function
    :param data: 
    :return: 
    """
    results = defaultdict(dict)
    for lat in range(10):
        for lon in range(10):
            results[lat][lon] = process_lat_lon(lat, lon, data)
    return dict(results)


if __name__ == "__main__":
    """Windows下必須在__main__命名下,所以如果調用,也都遵循這個規則吧"""
    random.seed(0)
    data = [random.random() for _ in range(200)]
    start = time.time()
    print(process_data_set(data))
    print(time.time() - start)

實際使用多線程完整模板:

import deco
import time
import random
from collections import defaultdict


@deco.concurrent.threaded(processes=16)
def process_lat_lon(lat, lon, data):
    """
    We add this for the concurrent function
    :param lat:
    :param lon:
    :param data:
    :return:
    """
    time.sleep(0.1)
    return data[lat + lon]


@deco.synchronized
def process_data_set(data):
    """
    And we add this for the function which calls the concurrent function
    :param data:
    :return:
    """
    results = defaultdict(dict)
    for lat in range(10):
        for lon in range(10):
            results[lat][lon] = process_lat_lon(lat, lon, data)
    return dict(results)


if __name__ == "__main__":
    """Windows下必須在__main__命名下,所以如果調用,也都遵循這個規則吧"""
    random.seed(0)
    data = [random.random() for _ in range(200)]
    start = time.time()
    print(process_data_set(data))
    print(time.time() - start)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章