【python】基礎知識鞏固(十)

heart_6662 2022-01-08 06:32:24 阅读数:306

python

目錄

進程和線程的概念

‍️補充:

‍️區別:

多進程

‍️multiprocessing

 ‍️代碼解讀

1)join()方法

2)start()方法

3)Process([group [, target [, name [, args [, kwargs]]]]])

4)getpid

 Pool進程池

 ‍️代碼解讀:

1.apply_async(func[, args[, kwds[, callback[, error_callback]]]])

2)注意

子進程 

 進程間通信

多線程 

‍️分類:

例如:

‍️線程與進程的區別 

代碼說明:

問題解决: 


進程和線程的概念

進程是資源分配的基本單比特,它是程序執行時的一個實例

在程序運行時創建;線程程序執行的最小單比特,是進程的一個執行流

簡單來說線程是進程中的一部分,進程包含多個線程在運行

‍️補充:

一個線程由多個協程組成的

‍️區別:

線程和進程的區別在於,子進程和父進程有不同的代碼和數據空間,而多個線程則共享數據空間,每個線程有自己的執行堆棧和程序計數器為其執行上下文。

多進程

python內置os模塊,讓Python程序中輕松創建子進程

‍️multiprocessing

由於Python是跨平臺的,自然也應該提供一個跨平臺的多進程支持。multiprocessing模塊就是跨平臺版本的多進程模塊,不管window還是mac,unix都可用

例如:啟動一個子進程並等待其結束:

from multiprocessing import Process
import os
# 子進程要執行的代碼
def run_proc(name):
print('Run child process %s (%s)...' % (name, os.getpid()))
if __name__=='__main__':
print('Parent process %s.' % os.getpid())
p = Process(target=run_proc, args=('test',))
print('Child process will start.')
p.start()
p.join()
print('Child process end.')

 output:

 ‍️代碼解讀

1)join()方法

當一個線程操作需要等待另一個線程執行完畢之後才能繼續進行時,使用Join()方法。Join方法會等到使用該方法的線程結束後再執行下面的代碼

2)start()方法

開始線程

3)Process([group [, target [, name [, args [, kwargs]]]]])

注意:1. 必須使用關鍵字方式來指定參數;

2. args指定的為傳給target函數的比特置參數,是一個元祖形式,必須有逗號

1.group應該是None;為將來ThreadGroup實現類時的擴展保留 。

2.targetrun()方法要調用的可調用對象。默認為None,意味著什麼都不調用。

3.name是線程名稱。默認情况下,唯一名稱的構造形式為“Thread- N ”,其中N是小十進制數,或者“Thread- N (target)”,其中“target”是指定target.__name__了 目標參數的情况。

4.args是目標調用的參數元組。默認為().

5.kwargs是目標調用的關鍵字參數字典。默認為{}.

6.如果不是None守護進程顯式設置線程是否是守護進程。如果None(默認),守護進程屬性是從當前線程繼承的。

4)getpid

返回進程ID。在進程產生之前,這將是 None.

 Pool進程池

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(4)
for i in range(5):
p.apply_async(long_time_task, args=(i,))
print('Waiting for all subprocesses done...')
p.close()
p.join()
print('All subprocesses done.')

 ‍️代碼解讀:

1.apply_async(func[, args[, kwds[, callback[, error_callback]]]])

apply() 方法的一個變種,返回一個 AsyncResult 對象。

如果指定了 callback , 它必須是一個接受單個參數的可調用對象。

當執行成功時, callback 會被用於處理執行後的返回結果,否則,調用 error_callback 。

如果指定了 error_callback , 它必須是一個接受單個參數的可調用對象。當目標函數執行失敗時, 會將拋出的异常對象作為參數傳遞給 error_callback 執行。

回調函數應該立即執行完成,否則會阻塞負責處理結果的線程。


2)注意

調用join()之前必須先調用close(),調用close()之後就不能繼續添加新的Process

子進程 

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

"""
CSDN : heart_6662
PYTHON amateur
"""
import subprocess
print('$ nslookup www.python.org')
r = subprocess.call(['nslookup', 'www.python.org'])
print('Exit code:', r)

 output:

 進程間通信

Process之間肯定是需要通信的,操作系統提供了很多機制來實現進程間的通信。Python的multiprocessing模塊包裝了底層的機制,提供了QueuePipes等多種方式來交換數據。

Queue為例,在父進程中創建兩個子進程一個往Queue裏寫數據,一個從Queue裏讀數據:

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__=='__main__':
# 父進程創建Queue,並傳給各個子進程:
q = Queue()
pw = Process(target=write, args=(q,))
pr = Process(target=read, args=(q,))
# 啟動子進程pw,寫入:
pw.start()
# 啟動子進程pr,讀取:
pr.start()
# 等待pw結束:
pw.join()
# pr進程裏是死循環,無法等待其結束,只能强行終止:
pr.terminate()

 

多線程 

多任務可以由多進程完成,也可以由一個進程內的多線程完成

很多高級語言都內置線程,而Python的線程是真正的Posix Thread,而不是模擬出來的線程

‍️分類:

_threadthreading_thread是低級模塊,threading是高級模塊,對_thread進行了封裝。絕大多數情况下,我們只需要使用threading這個高級模塊。

例如:

啟動一個線程就是把一個函數傳入並創建Thread實例,然後調用start()開始執行

import time, threading
# 新線程執行的代碼:
def loop():
print('thread %s is running...' % threading.current_thread().name)
n = 0
while n < 5:
n = n + 1
print('thread %s >>> %s' % (threading.current_thread().name, n))
time.sleep(1)
print('thread %s ended.' % threading.current_thread().name)
print('thread %s is running...' % threading.current_thread().name)
t = threading.Thread(target=loop, name='LoopThread')
t.start()
t.join()
print('thread %s ended.' % threading.current_thread().name)

 

‍️線程與進程的區別 

                                                          就是內容共享問題

 多線程和多進程最大的不同在於,多進程中,同一個變量,各自有一份拷貝存在於每個進程中,互不影響,

多線程中,所有變量都由所有線程共享,所以,任何一個變量都可以被任何一個線程修改,因此,線程之間共享數據最大的危險在於多個線程同時改一個變量,把內容給改亂

例如: 

import time, threading
# 假定這是你的銀行存款:
balance = 0
def change_it(n):
# 先存後取,結果應該為0:
global balance
balance = balance + n
balance = balance - n
def run_thread(n):
for i in range(2000000):
change_it(n)
t1 = threading.Thread(target=run_thread, args=(5,))
t2 = threading.Thread(target=run_thread, args=(8,))
t1.start()
t2.start()
t1.join()
t2.join()
print(balance)

代碼說明:

定義了一個共享變量balance,初始值為0,並且啟動兩個線程,先存後取,理論上結果應該為0 

                                                                可結果為-3

原因:

因為高級語言的一條語句在CPU執行時是若幹條語句,即使一個簡單的計算;例如

balance = balance + n

也分兩步:

  1. 計算balance + n,存入臨時變量中;
  2. 將臨時變量的值賦給balance
x = balance + n
balance = x

 

問題解决: 

                                                                   核心在於限制

如果我們要確保balance計算正確,就要給change_it()上一把鎖,當某個線程開始執行change_it()時,該線程因為獲得了鎖,因此其他線程不能同時執行change_it(),直到鎖被釋放後,獲得該鎖以後才能改。

由於鎖只有一個,無論多少線程,同一時刻最多只有一個線程持有該鎖,所以,不會造成修改的沖突。

                           創建一個鎖就是通過threading.Lock()來實現將代碼加上我們的鎖:

"""
CSDN : heart_6662
PYTHON amateur
"""
import time, threading
# 假定這是你的銀行存款:
balance = 0
def change_it(n):
# 先存後取,結果應該為0:
global balance
balance = balance + n
balance = balance - n
balance = 0
lock = threading.Lock()
def run_thread(n):
for i in range(100000):
# 先要獲取鎖:
lock.acquire()
try:
# 放心地改吧:
change_it(n)
finally:
# 改完了一定要釋放鎖:
lock.release()
t1 = threading.Thread(target=run_thread, args=(5,))
t2 = threading.Thread(target=run_thread, args=(8,))
t1.start()
t2.start()
t1.join()
t2.join()
print(balance)

不管怎麼運行,你的結果這次一次為0

版权声明:本文为[heart_6662]所创,转载请带上原文链接,感谢。 https://gsmany.com/2022/01/202201080632235993.html