Python學習之旅(二十三)

finsom發表於2018-12-04

Python基礎知識(22):程式和執行緒(Ⅰ)

1、多程式

(1)fork

Python的os模組封裝了常見的系統呼叫,其中就包括fork,可以在Python程式中輕鬆建立子程式

fork可以在Mac的Python上執行,但無法再Windows下執行

(2)multiprocess

multiprocessing模組就是跨平臺版本的多程式模組

multiprocessing模組提供了一個Process類來代表一個程式物件

#process_1.py

from multiprocessing import Process
import os

def work(name):
    print("Run child process %s(%s)..." %(name,os.getpid()))

if __name__=="__main__":
    print("Parent process %s." % os.getpid())
    #建立程式例項
    p = Process(target=work, args=("test",))
    print("Child process will start...")
    p.start()
    p.join()
    print("Child process end.")

結果:
Parent process 14628.
Child process will start...
Child process end.

 

建立子程式時,只需要傳入一個執行函式和函式的引數,建立一個Process例項,用start()方法啟動,join()方法可以等待子程式結束後再繼續往下執行,通常用於程式間的同步

(3)pool

用程式池的方式批量建立子程式,啟動大量的子程式

#process_2.py

from multiprocessing import Pool
import os, time, random

def long_time_task(name):
    print("Run task %s(%s)..." %(name,os.getpid()))
    start=time.time()
    time.sleep(random.random()*3)
    end=time.time()
    print("Task &s runs %0.2f seconds." %(name,(end - start)))

if __name__=="__main__":
    print("Parent process %s." % os.getpid())
    p = Pool(2)
    for i in range(3):
        p.apply_async(long_time_task, args=(i,))
    print("Waiting for all subprocess done...")
    p.close()
    p.join()
    print("All subprocess done")

結果:
Parent process 2096.
Waiting for all subprocess done...
All subprocess done

Pool的預設大小是CPU的核數,此次執行環境cup核數為1

(4)子程式

subprocess模組可以讓我們非常方便地啟動一個子程式,然後控制其輸入和輸出

#process_3.py

import subprocess

print("$ nslookup www.python.org")
r = subprocess.call(["nslookup", "www.python.org"])
print("Exit code:", r)


結果:
$ nslookup www.python.org
Exit code: 0

 如果子程式還需要輸入,則可以通過communicate()方法

(5)程式間通訊

Python的multiprocessing模組包裝了底層的機制,提供了QueuePipes等多種方式來交換資料

#process_4.py

from multiprocessing import Process, Queue
import os, time, random

def write(q):
    print("Process to write: %s" %os.getpid())
    for value in ["A","B","C"]:
        print("Put %s to queue..." % value)
        q.put(value)
        time.sleep(random.random())

def read(q):
    print("Process to read: %s" % os.getpid())
    while True:
        value = q.get(True)
        print("Get %s from queue." % value)

if __name__=="__mainn__":
    q = Queue()
    pw = Process(target=write, args=(q,))
    pr = Process(target=read, args=(q,))
    pw.start()
    pr.start()
    pw.join()
    pr.terminate()

二、多執行緒

多工可以由多程式完成,也可以由一個程式內的多執行緒完成

程式是由若干執行緒組成的,一個程式至少有一個執行緒

Python的標準庫提供了兩個模組:_threadthreading_thread是低階模組,threading是高階模組,對_thread進行了封裝

絕大多數情況下,我們只需要使用threading這個高階模組

import time, threading

def work():
    n = 1
    while n < 6:
        print("Work %s is running..." % str(n))
        n+=1

t = threading.Thread(target = work, name = "workThread")
t.start()
t.join()
print("%s ended." % threading.current_thread().name)


結果:
Work 1 is running...
Work 2 is running...
Work 3 is running...
Work 4 is running...
Work 5 is running...
MainThread ended.

由於任何程式預設就會啟動一個執行緒,我們把該執行緒稱為主執行緒,主執行緒又可以啟動新的執行緒,Python的threading模組有個current_thread()函式,它永遠返回當前執行緒的例項

主執行緒例項的名字叫MainThread,子執行緒的名字在建立時指定,名字僅僅在列印時用來顯示,完全沒有其他意義,如果不起名字Python就自動給執行緒命名為Thread-1Thread-2……

LOCK

執行緒中,所有變數都由所有執行緒共享,所以,任何一個變數都可以被任何一個執行緒修改

執行緒之間共享資料最大的危險在於多個執行緒同時改一個變數,把內容給改亂了

當某個程式要更改資料時,先給它上鎖,其它執行緒不能更改。只有當鎖被釋放後,其它執行緒獲得該鎖以後才能改

由於鎖只有一個,無論多少執行緒,同一時刻最多隻有一個執行緒持有該鎖,所以,不會造成修改的衝突

多核CPU

Python雖然不能利用多執行緒實現多核任務,但可以通過多程式實現多核任務。多個Python程式有各自獨立的GIL鎖,互不影響

相關文章