一:多進程
進程(process),是計算機中已運行程序的實體,是線程的容器;一個進程至少有一個線程
1,父進程與其子進程:
各進程的內存空間是互相隔離的
進程創建時,爲該進程生成一個PCB(進程控制塊);進程終止時,回收PCB。每個進程都有一個非負的唯一進程ID(PID)。雖然是唯一的,但是PID可以重用,當一個進程終止後,其他進程就可以使用它的PID了。
linux:調用fork創建子進程
PID爲0的進程爲調度進程,該進程是內核的一部分,也稱爲系統進程;PID爲1的進程爲init進程,它是一個普通的用戶進程,但是以超級用戶特權運行;PID爲2的進程是頁守護進程,負責支持虛擬存儲系統的分頁操作。
子進程剛開始,內核並沒有爲它分配物理內存,而是以只讀的方式共享父進程內存,只有當子進程寫時,才分配物理內存,複製父進程的數據給子進程,即“copy-on-write”
windows:調用CreateProcess創建子進程
子進程創建時就爲它分配物理內存,複製父進程的數據給子進程
殭屍進程:子進程結束了,父進程還未結束,此時,子進程的一些資源(如PID)還未回收,需要等到父進程結束才能回收,就稱這樣的子進程爲殭屍進程
孤兒進程(linux):父進程結束了,子進程還在,此時,這些子進程託管給init進程來管理
2,創建子進程的兩種方式:
from multiprocessing import Process
import os
# multiprocessing 是一個包,threading是一個模塊,它倆有着相似的API
#方式一:自定義創建子進程的類,繼承於Process類
class Myprocess(Process):
def __init__(self,runner):
super().__init__()
self.runner = runner
self.name = '子進程' # 自定義子進程的名字,默認爲Myprocess-1,Myprocess-2
def run(self): # 子進程的任務函數,自動執行,名字必須爲run
print('%s is running' % self.runner)
print(self.name)
print('PID爲:%s,其父進程PID爲:%s' %(os.getpid(),os.getppid()))
if __name__ == '__main__':
subprocess_obj1 = Myprocess('rock')
subprocess_obj2 = Myprocess('mike')
subprocess_obj1.start()
subprocess_obj2.start()
# 方式二:使用Process類
def task(name):
print('%s is running' %name)
print('PID爲:%s,其父進程PID爲:%s' %(os.getpid(),os.getppid()))
if __name__ == '__main__':
# 必須寫在 if __name__ == '__main__': 裏面
subprocess_obj = Process(target=task,args=('rock',),kwargs={},name='自定義進程名字')
subprocess_obj.start() # 向操作系統申請開啓該子進程
subprocess_obj.daemon = True # 把該子進程對象設置爲守護進程
# 守護進程--被守護的進程代碼一執行完守護進程就結束
print(subprocess_obj.name) # 取得該子進程對象的名字
print(subprocess_obj.pid) # 取得該子進程對象的PID
subprocess_obj.join() # 進入阻塞狀態,等待該子進程執行結束
subprocess_obj.is_alive() # 判斷該子進程對象是否仍存活
subprocess_obj.terminate() # 向操作系統申請結束該子進程
print('嘿,我是父進程,我的PID爲%s' %os.getpid())
3,Queue 與 JoinableQueue
IPC機制:inter-process communication 進程間通信機制
主要有管道,隊列等。。。
Queue 與 JoinableQueue:
共享的內存空間;有互斥鎖機制,以保障共享數據的安全
from multiprocessing import Queue,JoinableQueue
# Queue
# Returns a process shared queue implemented using a pipe and a few locks/semaphores
q = Queue(5) # 隊列對象,參數5表示隊列中能存的值的個數
q.put('abc') # 往隊列裏面存值;當隊列滿了時,這行語句執行被阻塞
q.get() # 從隊列裏面取值;當隊列爲空時,這行語句執行被阻塞
# JoinableQueue
# JoinableQueue, a Queue subclass, is a queue which additionally has task_done() and join() methods.
q1 = JoinableQueue()
q1.put('abc')
q1.get()
# task_done()用於與join()搭配使用:
q1.task_done() # 隊列每被取走一個值就通知一次隊列,通過這種方式能知道隊列的值是否被取完
q1.join() # 處於阻塞狀態,等待隊列所有值被取走
二:多線程
線程(thread)是操作系統能夠進行運算調度的最小單位。它被包含在進程之中,是進程中的實際運作單位。
1,進程與其線程:
一個進程下的所有線程共享該進程的全部資源
2,創建子線程的兩種方式:
from threading import Thread,current_thread
#方式一:自定義創建子線程的類,繼承於Thread類
class Mythread(Thread):
def __init__(self,runner):
super().__init__()
self.runner = runner
# self.name = '子線程' # 自定義子線程的名字,默認爲Thread-1,Thread-2
def run(self): # 子線程的任務函數,自動執行,名字必須爲run
print('%s is running' % self.runner)
print('my name is %s' %current_thread().name)
if __name__ == '__main__':
subthread_obj1 = Mythread('rock')
subthread_obj2 = Mythread('mike')
subthread_obj1.start()
subthread_obj2.start()
# 方式二:使用Process類
def task(name):
print('%s is running' %name)
print('my name is %s' %current_thread().name) # Thread-1
if __name__ == '__main__':
subthread_obj = Thread(target=task,args=('rock',))
subthread_obj.start() # 向操作系統申請開啓該子線程
print(subthread_obj.getName())
print(subthread_obj.name) # 取得該子線程對象的名字
subthread_obj.daemon = True # 設置該子線程對象爲守護線程
# 守護線程--被守護的線程結束(代碼執行完不一定馬上就結束)守護線程才結束
subthread_obj.join() # 進入阻塞狀態,等待該子線程執行結束
subthread_obj.is_alive() # 判斷該子線程對象是否仍存活
print('嘿,我是主線程,我的名字是%s' %current_thread().name) # MainThread
3,線程隊列 queue
import queue
# The queue module implements multi-producer, multi-consumer queues. It is especially useful
# in threaded programming when information must be exchanged safely between multiple threads.
q = queue.Queue() # 普通隊列 先進先出
q.put(1)
q.get()
# task_done()用於與join()搭配使用:
q.task_done() # 隊列每被取走一個值就通知一次隊列,通過這種方式能知道隊列的值是否被取完
q.join() # 處於阻塞狀態,等待隊列所有值被取走
q1 = queue.LifoQueue() # 棧隊列 先進後出
q1.put('a')
q1.get()
q1.task_done()
q1.join()
q2 = queue.PriorityQueue() # 優先級隊列,優先級最高的先出來
q2.put((1,'abc')) # 參數格式:(priority_number, data) 數字越小優先級越高
q2.get()
q2.task_done()
q2.join()
三:三種鎖的介紹
# 1,互斥鎖
from multiprocessing import Lock
# from threading import Lock
lock = Lock() # 創建互斥鎖對象,該對象有兩種狀態:locked 和 unlocked ;初始狀態爲 unlocked
def task(lock):
lock.acquire() # acquire(block=True, timeout=None)
# 實現原理:
# 當鎖對象是unlocked狀態時,該函數調用正常,並立即將鎖對象的狀態轉爲locked狀態;
# 當鎖對象是locked狀態時,該函數調用被阻塞
pass
lock.release() # 把鎖對象的狀態由locked 轉爲 unlocked。若鎖對象已經是unlocked狀態時,會報錯
# 2,遞歸鎖
# from multiprocessing import RLock
from threading import RLock
r_lock = RLock() # 這個類實現了可重入的鎖對象,其初始遞歸級別爲0
r_lock.acquire()
# 某線程第一次調用該函數時:
# 當鎖對象的遞歸級別爲0時,該函數調用正常,且立即將遞歸級別加1;其後該線程可多次調用該函數,每次將遞歸級別加1
# 當鎖對象的遞歸級別不爲0時,阻塞
r_lock.release()
# 將鎖對象的遞歸級別減1
# 3,信號量
# from multiprocessing import Semaphore
from threading import Semaphore
s_lock = Semaphore(5) # 產生信號量對象,內部計數器初始值爲5;不傳值則默認值爲1
s_lock.acquire()
# Acquire a semaphore
# 當內部計數器值大於0時,該函數調用正常,且內部計數器立即執行減1操作;
# 當內部計數器值爲0時,該函數調用被阻塞,直到內部計數器大於0時,恢復調用正常,內部計數器減1
s_lock.release()
# Release a semaphore
# 內部計數器立即執行加1操作
# 各種鎖可以使用with聲明語句,語法如下:
with some_lock:
# do something...
# 其等同於:
some_lock.acquire()
try:
# do something...
finally:
some_lock.release()
四:進程池與線程池
進程池:裝子進程的池子,能控制子進程的個數,是一種保護機制,確保進程個數在機器性能可承受範圍內
線程池:裝子線程的池子,能控制子線程的個數,是一種保護機制,確保線程個數在機器性能可承受範圍內
from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
# ProcessPoolExecutor
# An Executor subclass that executes calls asynchronously using a pool of at most max_workers processes.
# ThreadPoolExecutor
# An Executor subclass that uses a pool of at most max_workers threads to execute calls asynchronously
def task(n):
n +=1
return n
def fn(future):
res = future.result()
pass
executor = ThreadPoolExecutor(5) # 可創建的子線程數量最大爲5,不傳參數則默認爲機器的cpu數*5
future = executor.submit(task,3)
# submit(fn, *args, **kwargs) 給線程池中的線程提交任務fn,即執行fn(*args, **kwargs);
# 返回一個表示可被調用的執行的Future對象
future.result() # 取得任務執行完畢的返回值
future.add_done_callback(fn) # 回調函數
# 把可調函數fn綁定給future對象;當future被取消或執行完畢時,fn會被調用(future作爲fn的唯一參數)
executor.shutdown()
# 等當前所有掛起的future對象執行完再回收所有的資源
# You can avoid having to call this method explicitly if you use the with statement,
# which will shutdown the Executor
with ThreadPoolExecutor(5) as e:
e.submit(task,3)
e.submit(task,4)
# 進程池與線程池的使用方式基本相同;
# 不同點:進程池,父進程執行回調函數綁定的函數fn(因爲future是父進程的變量);
# 線程池,某個線程繼續執行函數fn
if __name__ == '__main__':
p = ProcessPoolExecutor(5) # 可創建的子進程數量最大爲5,不傳參數則默認爲機器的cpu數
future = p.submit(task,4)
print(future.result()) #Return the value returned by the call.