Python學習筆記(十二)ThreadLocal及分佈式進程

參考資料:https://www.liaoxuefeng.com/wiki/001374738125095c955c1e6d8bb493182103fac9270762a000/001386832845200f6513494f0c64bd882f25818a0281e80000

1、ThreadLocal:提供在線程間獨立操作外部變量的方式。直接看代碼:

import threading

localobj = threading.local()

def print_student():
    print "student name in thread(%s) is %s" % (threading.current_thread().name, localobj.student)


def process_thread(name):
    localobj.student = name
    print_student()
#測試TheadLocal特性
def Test():
    localobj.student = 'MyName'
    t1 = threading.Thread(target=process_thread, args=('Tom',), name = 'ThreadA')
    t2 = threading.Thread(target=process_thread, args=('Alice',), name = 'ThreadB')
    t1.start()
    t2.start()
    t1.join()
    t2.join()
    print "student name in mainthread is", localobj.student

2、在Thread和Process中,應當優選Process,因爲Process更穩定,而且,Process可以分佈到多臺機器上,而Thread最多隻能分佈到同一臺機器的多個CPU上(Python不支持)。

3、分佈式進程:Python的multiprocessing模塊不但支持多進程,其中managers子模塊還支持把多進程分佈到多臺機器上。一個服務進程可以作爲調度者,依靠網絡通信將任務分佈到其他多個進程中。參考資料中給出的代碼在Windows操作系統下會報錯,下面是基於參考資料代碼修改後在Windows操作系統下測試通過的代碼

(1)服務器進程:用於發佈任務和蒐集分佈式運行結果。

# -*- coding: utf-8 -*-
import random, time, Queue
from multiprocessing.managers import BaseManager
from multiprocessing import *

# 用於網絡共享的任務序列和結果隊列
task_queue = Queue()
result_queue = Queue()

# 從BaseManager繼承的QueueManager:
class QueueManager(BaseManager):
    pass
# 注意:下面的2個方法是針對Windows操作系統所作的修改,用於適應Lambda表達式無法Picle的問題(序列化)
def getTaskQueue():
    #print 'getTaskQueue'
    global task_queue
    return task_queue

def getResultQueue():
    #print 'getResultQueue'
    global result_queue
    return result_queue
#定義一個方法,用於發佈任務
def putTask(manager):
    # 獲得通過網絡訪問的Queue對象:
    try:
        task = manager.get_task_queue()
    except BaseException, e:
        print 'Failure to get TaskQueue:', e.message
        return 0
    # 放幾個任務進去:
    for i in range(10):
        n = random.randint(0, 10000)
        print('Put task %d...' % n)
        task.put(n)
#定義一個方法,用於蒐集結果
def getResult(manager):
    # 獲得通過網絡訪問的Queue對象:
    try:
        result = manager.get_result_queue()
    except BaseException, e:
        print 'Failure to get ResultQueue:', e.message
        return 0
    # 從result隊列讀取結果:
    print('Try get results...')
    for i in range(10):
        try:
            r = result.get(timeout=10)
            print('Result: %s' % r)
        except BaseException, e:
            print 'Result Queue is Empty.', e.message
#主要的測試方法
def Test():   
    # 把兩個Queue都註冊到網絡上, callable參數關聯了Queue對象:原來的代碼用到Lambda表達式,現在改成了自定義方法
    QueueManager.register('get_task_queue', callable = getTaskQueue)
    QueueManager.register('get_result_queue', callable = getResultQueue)
    # 綁定端口5000, 設置驗證碼'abc':原來的代碼採用明碼,現在改爲b'abc'。
    manager = QueueManager(address=('127.0.0.1', 5000), authkey=b'abc')
    # 啓動Queue:
    manager.start()
    #manager.get_server().server_forever()
    putTask(manager)
    getResult(manager)
    manager.shutdown() 

if __name__ == '__main__':
    freeze_support()
    Test()

(2)客戶端進程:接收任務,並將本地處理結果寫入共享的結果隊列。

# -*- coding: utf-8 -*-
import time, sys, Queue
from multiprocessing.managers import BaseManager
from multiprocessing import *

# 創建類似的QueueManager:
class QueueManager(BaseManager):
    pass

# 由於這個QueueManager只從網絡上獲取Queue,所以註冊時只提供名字:
QueueManager.register('get_task_queue')
QueueManager.register('get_result_queue')

def Test():
    # 連接到服務器,也就是運行taskmanager.py的機器:
    server_addr = raw_input('input server address:')
    if server_addr == '':
        server_addr = '127.0.0.1'
    print('Connect to server %s...' % server_addr)
    # 端口和驗證碼注意保持與服務器進程設置的完全一致:
    m = QueueManager(address=(server_addr, 5000), authkey=b'abc')
    # 從網絡連接:
    m.connect()
    # 獲取Queue的對象:
    task = m.get_task_queue()
    result = m.get_result_queue()
    # 從task隊列取任務,並把結果寫入result隊列:
    for i in range(10):
        try:
            n = task.get(timeout=1)
            print('run task %d * %d...' % (n, n))
            r = '%d * %d = %d' % (n, n, n*n)
            time.sleep(1)
            result.put(r)
        except BaseException:
            print 'task queue is empty.'
    # 處理結束:
    print('worker exit.')

if __name__ == '__main__':
    freeze_support()
    Test()

(3)先在服務器上運行服務器進程,然後在客戶端運行客戶端進程,客戶端進程監聽服務器任務隊列並根據隊列信息執行本地任務,將執行結果寫入結果隊列;服務器進程監聽結果隊列,將結果隊列內容從服務器輸出。也可在同一臺機器上同時分別運行2個進程。

今天就學習到這裏啦,下一節從正則表達式學起。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章