并發(fā)性和并行性都用于多線程程序,但它們之間的相似性和差異存在很多混淆。 在這方面的一個(gè)大問(wèn)題是:并發(fā)是不是就是并行? 雖然這兩個(gè)術(shù)語(yǔ)看起來(lái)很相似,但對(duì)上述問(wèn)題的答案是否定的,并發(fā)性和并行性并不相同。 現(xiàn)在,如果它們不一樣,它們之間的基本區(qū)別是什么?
簡(jiǎn)而言之,并發(fā)處理的是處理來(lái)自不同線程的共享狀態(tài)訪問(wèn),而并行處理利用多個(gè)CPU或其內(nèi)核來(lái)提高硬件性能。
并發(fā)是兩個(gè)任務(wù)在執(zhí)行過(guò)程中重疊的時(shí)候。 這可能是一個(gè)應(yīng)用程序正在同時(shí)處理多個(gè)任務(wù)的情況。 我們可以用圖解理解它; 多項(xiàng)任務(wù)正在同時(shí)取得進(jìn)展,如下所示 -

在本節(jié)中,我們將討論編程方面的三個(gè)重要級(jí)別的并發(fā)性 -
1. 低級(jí)并發(fā)
在這種并發(fā)級(jí)別中,顯式使用了原子操作。 我們不能在構(gòu)建應(yīng)用程序時(shí)使用這種并發(fā)性,因?yàn)樗浅H菀壮鲥e(cuò)并且很難調(diào)試。 即使Python不支持這種并發(fā)性。
2. 中級(jí)并發(fā)
在這種并發(fā)中,沒(méi)有使用顯式的原子操作。 它使用顯式鎖。 Python和其他編程語(yǔ)言支持這種并發(fā)性。 大多數(shù)應(yīng)用程序員使用這種并發(fā)性。
3. 高級(jí)并發(fā)
在這種并發(fā)中,不使用顯式原子操作也不使用顯式鎖。 Python有concurrent.futures模塊來(lái)支持這種并發(fā)。
要使程序或并發(fā)系統(tǒng)正確,一些屬性必須由它來(lái)滿足。 與終止系統(tǒng)相關(guān)的屬性如下 -
正確性屬性
正確性屬性意味著程序或系統(tǒng)必須提供所需的正確答案。 為了簡(jiǎn)單起見(jiàn),可以說(shuō)系統(tǒng)必須正確地將啟動(dòng)程序狀態(tài)映射到最終狀態(tài)。
安全屬性
安全屬性意味著程序或系統(tǒng)必須保持“良好”或“安全”狀態(tài),并且從不做任何“壞”的事情。
活躍度屬性
這個(gè)屬性意味著一個(gè)程序或系統(tǒng)必須“取得進(jìn)展”,并且會(huì)達(dá)到一個(gè)理想的狀態(tài)。
并發(fā)系統(tǒng)的行為者
這是并發(fā)系統(tǒng)的一個(gè)常見(jiàn)屬性,其中可以有多個(gè)進(jìn)程和線程,它們同時(shí)運(yùn)行以在他們自己的任務(wù)上取得進(jìn)展。 這些進(jìn)程和線程稱為并發(fā)系統(tǒng)的角色。
并發(fā)系統(tǒng)的資源
行為者必須利用內(nèi)存,磁盤(pán),打印機(jī)等資源來(lái)執(zhí)行任務(wù)。
某些規(guī)則集
每個(gè)并發(fā)系統(tǒng)必須擁有一套規(guī)則來(lái)定義執(zhí)行者要執(zhí)行的任務(wù)類型和每個(gè)任務(wù)的執(zhí)行時(shí)間。 任務(wù)可能是獲取鎖,共享內(nèi)存,修改狀態(tài)等。
在實(shí)現(xiàn)并發(fā)系統(tǒng)時(shí),程序員必須考慮以下兩個(gè)重要問(wèn)題,這可能是并發(fā)系統(tǒng)的障礙 -
共享數(shù)據(jù)
實(shí)現(xiàn)并發(fā)系統(tǒng)時(shí)的一個(gè)重要問(wèn)題是在多個(gè)線程或進(jìn)程間共享數(shù)據(jù)。 實(shí)際上,程序員必須確保鎖保護(hù)共享數(shù)據(jù),以便所有對(duì)它的訪問(wèn)都被序列化,并且一次只有一個(gè)線程或進(jìn)程可以訪問(wèn)共享數(shù)據(jù)。 如果多個(gè)線程或進(jìn)程都試圖訪問(wèn)相同的共享數(shù)據(jù),那么除了其中至少一個(gè)以外,其他所有進(jìn)程都將被阻塞并保持空閑狀態(tài)。 換句話說(shuō),在鎖定生效時(shí),我們只能使用一個(gè)進(jìn)程或線程。 可以有一些簡(jiǎn)單的解決方案來(lái)消除上述障礙 -
數(shù)據(jù)共享限制
最簡(jiǎn)單的解決方案是不共享任何可變數(shù)據(jù)。 在這種情況下,我們不需要使用顯式鎖定,并且可以解決由于相互數(shù)據(jù)而導(dǎo)致的并發(fā)障礙。
數(shù)據(jù)結(jié)構(gòu)協(xié)助
很多時(shí)候并發(fā)進(jìn)程需要同時(shí)訪問(wèn)相同的數(shù)據(jù)。 與使用顯式鎖相比,另一種解決方案是使用支持并發(fā)訪問(wèn)的數(shù)據(jù)結(jié)構(gòu)。 例如,可以使用提供線程安全隊(duì)列的隊(duì)列模塊。 也可以使用multiprocessing.JoinableQueue類來(lái)實(shí)現(xiàn)基于多處理的并發(fā)。
不可變的數(shù)據(jù)傳輸
有時(shí),我們使用的數(shù)據(jù)結(jié)構(gòu)(比如說(shuō)并發(fā)隊(duì)列)不適合,那么可以傳遞不可變數(shù)據(jù)而不鎖定它。
可變數(shù)據(jù)傳輸
繼續(xù)上面的解決方案,假設(shè)如果它只需要傳遞可變數(shù)據(jù)而不是不可變數(shù)據(jù),那么可以傳遞只讀的可變數(shù)據(jù)。
共享I/O資源
實(shí)現(xiàn)并發(fā)系統(tǒng)的另一個(gè)重要問(wèn)題是線程或進(jìn)程使用I/O資源。 當(dāng)一個(gè)線程或進(jìn)程使用I/O很長(zhǎng)時(shí)間而另一線程或進(jìn)程閑置時(shí)會(huì)出現(xiàn)問(wèn)題。 在處理I/O大量應(yīng)用程序時(shí),我們可以看到這種障礙。 可以通過(guò)一個(gè)例子來(lái)理解,從Web瀏覽器請(qǐng)求頁(yè)面。 這是一個(gè)沉重的應(yīng)用程序。 在這里,如果數(shù)據(jù)請(qǐng)求的速率比它消耗的速率慢,那么在并發(fā)系統(tǒng)中就會(huì)有I/O障礙。
以下Python腳本用于請(qǐng)求網(wǎng)頁(yè)并獲取網(wǎng)絡(luò)用于獲取請(qǐng)求頁(yè)面的時(shí)間 -
import urllib.request
import time
ts = time.time()
req = urllib.request.urlopen('http://www.yiibai.com')
pageHtml = req.read()
te = time.time()
print("Page Fetching Time : {} Seconds".format (te-ts))
執(zhí)行上述腳本后,可以獲取頁(yè)面獲取時(shí)間,如下所示。
Page Fetching Time: 0.999139881798332 Seconds
可以看到,獲取該頁(yè)面的時(shí)間差不多是一秒鐘。 現(xiàn)在,如果我們想要訪問(wèn)數(shù)千個(gè)不同的網(wǎng)頁(yè),您可以大概知道訪問(wèn)網(wǎng)絡(luò)需要多少時(shí)間。
并行可定義為將任務(wù)分解為可同時(shí)處理的子任務(wù)的技術(shù)。 如上所述,它與并發(fā)性相反,其中兩個(gè)或更多事件同時(shí)發(fā)生。 我們可以用圖解理解它; 一個(gè)任務(wù)被分解成可以并行處理的多個(gè)子任務(wù),如下所示 -
并行但不平行
應(yīng)用程序可以是并行的,但不是并行的,意味著它可以同時(shí)處理多個(gè)任務(wù),但任務(wù)不會(huì)分解為子任務(wù)。
并行但不并發(fā)
一個(gè)應(yīng)用程序可以是并行的,但不是并行的,意味著它一次只能在一個(gè)任務(wù)上工作,并且分解為子任務(wù)的任務(wù)可以并行處理。
既不平行也不并發(fā)
應(yīng)用程序既不能并行也不能并發(fā)。 這意味著它一次只能處理一項(xiàng)任務(wù),并且任務(wù)不會(huì)被分解為子任務(wù)。
并行和并發(fā)
應(yīng)用程序既可以是并行的,也可以是并行的,這意味著它既可以同時(shí)在多個(gè)任務(wù)上工作,也可以將任務(wù)分解為子任務(wù)并行執(zhí)行。
我們可以通過(guò)在單CPU的不同內(nèi)核之間或網(wǎng)絡(luò)內(nèi)連接的多臺(tái)計(jì)算機(jī)之間分配子任務(wù)來(lái)實(shí)現(xiàn)并行。
考慮以下要點(diǎn)來(lái)理解為什么有必要實(shí)現(xiàn)并行性 -
有效的代碼執(zhí)行
借助并行性,我們可以高效地運(yùn)行代碼。 它將節(jié)省時(shí)間,因?yàn)椴糠种械南嗤a并行運(yùn)行。
比順序計(jì)算更快速
順序計(jì)算受到物理和實(shí)際因素的限制,因此無(wú)法獲得更快的計(jì)算結(jié)果。 另一方面,這個(gè)問(wèn)題可以通過(guò)并行計(jì)算來(lái)解決,并且比順序計(jì)算提供更快的計(jì)算結(jié)果。
執(zhí)行時(shí)間更短
并行處理減少了程序代碼的執(zhí)行時(shí)間。
如果要談?wù)撜鎸?shí)生活中并行性的例子,我們計(jì)算機(jī)的圖形卡就是一個(gè)例子,它強(qiáng)調(diào)了并行處理的真正能力,因?yàn)樗鼡碛袛?shù)百個(gè)獨(dú)立工作的獨(dú)立處理內(nèi)核,并且可以同時(shí)執(zhí)行。 由于這個(gè)原因,我們也能夠運(yùn)行高端應(yīng)用程序和游戲。
我們知道并發(fā)性,并行性以及它們之間的差異,但是它將如何實(shí)現(xiàn)。 理解將要實(shí)施的系統(tǒng)是非常必要的,因?yàn)樗刮覀冊(cè)谠O(shè)計(jì)軟件時(shí)能夠做出明智的決定。有以下兩種處理器 -
單核處理器
單核處理器能夠在任何給定時(shí)間執(zhí)行一個(gè)線程。 這些處理器使用上下文切換來(lái)在特定時(shí)間存儲(chǔ)線程的所有必要信息,然后再恢復(fù)信息。 上下文切換機(jī)制有助于我們?cè)诮o定秒內(nèi)的多個(gè)線程上取得進(jìn)展,并且看起來(lái)好像系統(tǒng)正在處理多種事情。
單核處理器具有許多優(yōu)點(diǎn)。 這些處理器需要更少的功率,并且多個(gè)內(nèi)核之間沒(méi)有復(fù)雜的通信協(xié)議。 另一方面,單核處理器的速度有限,不適合更大的應(yīng)用。
多核處理器
多核處理器具有多個(gè)獨(dú)立處理單元,也稱為核心。
這種處理器不需要上下文切換機(jī)制,因?yàn)槊總€(gè)核心都包含執(zhí)行一系列存儲(chǔ)指令所需的所有內(nèi)容。
多核處理器的內(nèi)核遵循一個(gè)執(zhí)行周期。 這個(gè)周期被稱為讀取 - 解碼 - 執(zhí)行周期。 它涉及以下步驟 -
讀取
這是循環(huán)的第一步,它涉及從程序存儲(chǔ)器讀取指令。
解碼
最近讀取的指令將被轉(zhuǎn)換為一系列觸發(fā)CPU其他部分的信號(hào)。
執(zhí)行
這是獲取和解碼指令將被執(zhí)行的最后一步。 執(zhí)行結(jié)果將存儲(chǔ)在CPU寄存器中。
這里的一個(gè)優(yōu)勢(shì)是多核處理器的執(zhí)行速度比單核處理器的執(zhí)行速度快。 它適用于更大的應(yīng)用程序。 另一方面,多核之間的復(fù)雜通信協(xié)議是一個(gè)問(wèn)題。 多核需要比單核處理器需要更多的功率。