自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Go 并行性和并發(fā)性:有什么不同?

開發(fā) 后端
并發(fā)和并行,Go 剛發(fā)布時(shí),官方就不斷強(qiáng)調(diào)這兩點(diǎn)的不同??赡苄率忠廊幻院?。這次給大家弄一個(gè)系列,詳細(xì)講解并發(fā)和并行。

 大家好,我是程序員幽鬼。

并發(fā)和并行,Go 剛發(fā)布時(shí),官方就不斷強(qiáng)調(diào)這兩點(diǎn)的不同??赡苄率忠廊幻院?。這次給大家弄一個(gè)系列,詳細(xì)講解并發(fā)和并行。

軟件中的并行性是同時(shí)執(zhí)行指令。每種編程語(yǔ)言要么實(shí)現(xiàn)自己的庫(kù),要么提供語(yǔ)言級(jí)支持,如 Go。并行性允許軟件工程師通過(guò)在多個(gè)處理器上并行執(zhí)行任務(wù)來(lái)回避硬件的物理限制。

由于正確利用并行構(gòu)建的復(fù)雜性,應(yīng)用程序的并行性取決于構(gòu)建軟件的工程師的技能 。

并行任務(wù)示例:

  • 多人在餐廳點(diǎn)單
  • 雜貨店的多個(gè)收銀員
  • 多核 CPU

實(shí)際上,任何應(yīng)用程序都存在多層并行性。有應(yīng)用程序本身的并行度,由應(yīng)用程序開發(fā)者定義,還有 CPU 在操作系統(tǒng)編排的物理硬件上執(zhí)行的指令的并行度(或多路復(fù)用)。

1、并行性的構(gòu)建

應(yīng)用程序開發(fā)人員利用抽象來(lái)描述應(yīng)用程序的并行性。這些抽象在實(shí)現(xiàn)并行性但概念相同的每種語(yǔ)言中通常是不同的。例如,在 C 中,并行性是通過(guò)使用 pthreads 來(lái)定義的 ,而在 Go 中,并行性是通過(guò)使用 goroutines 來(lái)定義的 。

進(jìn)程

進(jìn)程是一個(gè)執(zhí)行單元,包括它自己的“程序計(jì)數(shù)器、寄存器和變量。從概念上講,每個(gè)進(jìn)程都有自己的虛擬 CPU”。理解這一點(diǎn)很重要,因?yàn)閯?chuàng)建和管理進(jìn)程會(huì)產(chǎn)生開銷。除了創(chuàng)建進(jìn)程的開銷外,每個(gè)進(jìn)程只能訪問(wèn)自己的內(nèi)存。這意味著該進(jìn)程無(wú)法訪問(wèn)其他進(jìn)程的內(nèi)存。

如果有多個(gè)執(zhí)行線程(并行任務(wù))需要訪問(wèn)某些共享資源,這將是一個(gè)問(wèn)題。

線程

引入線程是為了在同一進(jìn)程內(nèi)但在不同的并行執(zhí)行單元上授予對(duì)共享內(nèi)存的訪問(wèn)權(quán)限。線程幾乎是它們自己的進(jìn)程,但可以訪問(wèn)父進(jìn)程的共享地址空間。

線程的開銷遠(yuǎn)低于進(jìn)程,因?yàn)樗鼈儾槐貫槊總€(gè)線程創(chuàng)建一個(gè)新進(jìn)程,并且資源可以共享或重用。

以下是 Ubuntu 18.04 的示例,比較了 fork 進(jìn)程和創(chuàng)建線程的開銷:

  1. # Borrowed from https://stackoverflow.com/a/52231151/834319 
  2. # Ubuntu 18.04 start_method: fork 
  3. # ================================ 
  4. results for Process: 
  5.  
  6. count    1000.000000 
  7. mean        0.002081 
  8. std         0.000288 
  9. min         0.001466 
  10. 25%         0.001866 
  11. 50%         0.001973 
  12. 75%         0.002268 
  13. max         0.003365  
  14.  
  15. Minimum with 1.47 ms 
  16. ------------------------------------------------------------ 
  17.  
  18. results for Thread: 
  19.  
  20. count    1000.000000 
  21. mean        0.000054 
  22. std         0.000013 
  23. min         0.000044 
  24. 25%         0.000047 
  25. 50%         0.000051 
  26. 75%         0.000058 
  27. max         0.000319  
  28.  
  29. Minimum with 43.89 µs 
  30. ------------------------------------------------------------ 
  31. Minimum start-up time for processes takes 33.41x longer than for threads. 

臨界區(qū)

臨界區(qū)是進(jìn)程中各種并行任務(wù)所需的共享內(nèi)存區(qū)。這些部分可能是共享數(shù)據(jù)、類型或其他資源。

并行的復(fù)雜性

由于進(jìn)程的線程在相同的內(nèi)存空間中執(zhí)行,因此存在多個(gè)線程同時(shí)訪問(wèn)臨界區(qū)的風(fēng)險(xiǎn)。這可能會(huì)導(dǎo)致應(yīng)用程序中的數(shù)據(jù)損壞或其他意外行為。

當(dāng)多個(gè)線程同時(shí)訪問(wèn)共享內(nèi)存時(shí),會(huì)出現(xiàn)兩個(gè)主要問(wèn)題。

競(jìng)態(tài)條件

競(jìng)態(tài)條件是多個(gè)并行執(zhí)行線程在沒(méi)有任何保護(hù)的情況下直接讀取或?qū)懭牍蚕碣Y源。這可能導(dǎo)致存儲(chǔ)在資源中的數(shù)據(jù)可能被損壞或?qū)е缕渌馔庑袨榈那闆r。

例如,想象一個(gè)進(jìn)程,其中單個(gè)線程正在從共享內(nèi)存位置讀取值,而另一個(gè)線程正在將新值寫入同一位置。如果第一個(gè)線程在第二個(gè)線程寫入值之前讀取該值,則第一個(gè)線程將讀取舊值。

這會(huì)導(dǎo)致應(yīng)用程序未按預(yù)期運(yùn)行的情況。

死鎖

當(dāng)兩個(gè)或多個(gè)線程互相等待做某事時(shí),就會(huì)發(fā)生死鎖。這可能導(dǎo)致應(yīng)用程序掛起或崩潰。

例如,一個(gè)線程針對(duì)一個(gè)臨界區(qū)執(zhí)行等待滿足條件,而另一個(gè)線程針對(duì)同一臨界區(qū)執(zhí)行并等待來(lái)自另一個(gè)線程的條件滿足。如果第一個(gè)線程正在等待滿足條件,而第二個(gè)線程正在等待第一個(gè)線程,則兩個(gè)線程將永遠(yuǎn)等待。

當(dāng)試圖通過(guò)使用互斥鎖來(lái)防止競(jìng)爭(zhēng)條件時(shí),可能會(huì)發(fā)生第二種形式的死鎖。

屏障(Barriers)

屏障是同步點(diǎn),用于管理進(jìn)程內(nèi)多個(gè)線程對(duì)共享資源或臨界區(qū)的訪問(wèn)。

這些屏障允許應(yīng)用程序開發(fā)人員控制并行訪問(wèn),以確保不會(huì)以不安全的方式訪問(wèn)資源。

互斥鎖

互斥鎖是一種屏障,它一次只允許一個(gè)線程訪問(wèn)共享資源。這對(duì)于在讀取或?qū)懭牍蚕碣Y源時(shí)通過(guò)鎖定解鎖來(lái)防止競(jìng)爭(zhēng)條件很有用。

  1. // Example of a mutex barrier in Go 
  2. import ( 
  3.   "sync" 
  4.   "fmt" 
  5.  
  6. var shared string 
  7. var sharedMu sync.Mutex 
  8.  
  9. func main() { 
  10.  
  11.   // Start a goroutine to write to the shared variable 
  12.   go func() { 
  13.     for i := 0; i < 10; i++ { 
  14.       write(fmt.Sprintf("%d", i)) 
  15.     } 
  16.   }() 
  17.  
  18.   // read from the shared variable 
  19.   for i := 0; i < 10; i++ { 
  20.     read(fmt.Sprintf("%d", i)) 
  21.   } 
  22.  
  23. func write(value string) { 
  24.   sharedMu.Lock() 
  25.   defer sharedMu.Unlock() 
  26.  
  27.   // set a new value for the `shared` variable 
  28.   shared = value 
  29.  
  30. func read() { 
  31.   sharedMu.Lock() 
  32.   defer sharedMu.Unlock() 
  33.  
  34.   // print the critical section `shared` to stdout 
  35.   fmt.Println(shared) 

如果我們查看上面的示例,可以看到 shared 變量受到互斥鎖的保護(hù)。這意味著一次只有一個(gè)線程可以訪問(wèn)該 shared 變量。這確保了shared 變量不會(huì)被破壞并且行為可預(yù)測(cè)。

注意:使用互斥鎖時(shí),確保在函數(shù)返回時(shí)釋放互斥鎖至關(guān)重要。例如,在 Go 中,可以通過(guò)使用defer關(guān)鍵字來(lái)完成。這確保了其他線程(goroutine)可以訪問(wèn)共享資源。

信號(hào)量

信號(hào)量是一種屏障,它一次只允許一定數(shù)量的線程訪問(wèn)共享資源。這與互斥鎖的不同之處在于,可以訪問(wèn)資源的線程數(shù)不限于一個(gè)。

Go 標(biāo)準(zhǔn)庫(kù)中沒(méi)有信號(hào)量實(shí)現(xiàn)。但是可以使用通道來(lái)實(shí)現(xiàn)。

忙等待(busy waiting)

忙等待是一種線程等待滿足條件的技術(shù)。通常用于等待計(jì)數(shù)器達(dá)到某個(gè)值。

  1. // Example of Busy Waiting in Go 
  2. var x int 
  3.  
  4. func main() { 
  5.   go func() { 
  6.     for i := 0; i < 10; i++ { 
  7.       x = i 
  8.     } 
  9.   }() 
  10.  
  11.   for x != 1 { // Loop until x is set to 1 
  12.     fmt.Println("Waiting..."
  13.     time.Sleep(time.Millisecond * 100) 
  14.   }   

所以,忙等待需要一個(gè)循環(huán),該循環(huán)等待滿足讀取或?qū)懭牍蚕碣Y源的條件,并且必須由互斥鎖保護(hù)以確保正確的行為。

上述示例的問(wèn)題是循環(huán)訪問(wèn)不受互斥鎖保護(hù)的臨界區(qū)。這可能導(dǎo)致循環(huán)訪問(wèn)該值但它可能已被進(jìn)程的另一個(gè)線程更改的競(jìng)態(tài)條件。事實(shí)上,上面的例子也是競(jìng)態(tài)條件的一個(gè)很好的例子。這個(gè)應(yīng)用程序可能永遠(yuǎn)不會(huì)退出,因?yàn)椴荒鼙WC循環(huán)足夠快以讀取 x=1 時(shí)的值,這意味著循環(huán)永遠(yuǎn)不會(huì)退出。

如果我們用互斥鎖保護(hù)變量x,循環(huán)將被保護(hù),應(yīng)用程序?qū)⑼顺觯@仍然不完美,循環(huán)設(shè)置x仍然足夠快,可以在讀取值的循環(huán)執(zhí)行之前兩次命中互斥鎖(雖然不太可能)。

  1. import "sync" 
  2.  
  3. var x int 
  4. var xMu sync.Mutex 
  5.  
  6. func main() { 
  7.   go func() { 
  8.     for i := 0; i < 10; i++ { 
  9.       xMu.Lock() 
  10.       x = i 
  11.       xMu.Unlock() 
  12.     } 
  13.   }() 
  14.  
  15.   var value int 
  16.   for value != 1 { // Loop until x is set to 1 
  17.     xMu.Lock() 
  18.     value = x // Set value == x 
  19.     xMu.Unlock() 
  20.   }   

一般來(lái)說(shuō),忙等待不是一個(gè)好辦法。最好使用信號(hào)量或互斥鎖來(lái)確保臨界區(qū)受到保護(hù)。我們將介紹在 Go 中處理此問(wèn)題的更好方法,但它說(shuō)明了編寫“正確”可并行代碼的復(fù)雜性。

WaitGroup

WaitGroup 是確保所有并行代碼路徑在繼續(xù)之前已完成處理的方法。在 Go 中,這是通過(guò)使用標(biāo)準(zhǔn)庫(kù)中 sync.WaitGroup 來(lái)完成的。

  1. // Example of a `sync.WaitGroup` in Go 
  2. import ( 
  3.   "sync" 
  4.  
  5. func main() { 
  6.   var wg sync.WaitGroup 
  7.   var N int = 10 
  8.  
  9.   wg.Add(N) 
  10.   for i := 0; i < N; i++ { 
  11.     go func() { 
  12.       defer wg.Done() 
  13.        
  14.       // do some work       
  15.     }() 
  16.   } 
  17.  
  18.   // wait for all of the goroutines to finish 
  19.   wg.Wait() 

在上面的示例中,wg.Wait() 是一個(gè)阻塞調(diào)用。這意味著主線程將不會(huì)繼續(xù),直到所有 goroutine 中的 defer wg.Done() 都調(diào)用。在內(nèi)部,WaitGroup 是一個(gè)計(jì)數(shù)器,對(duì)于添加到wg.Add(N)調(diào)用的 WaitGroup 中的每個(gè) goroutine,它都會(huì)加一。當(dāng)計(jì)數(shù)器為零時(shí),主線程將繼續(xù)處理,或者在這種情況下應(yīng)用程序?qū)⑼顺觥?/p>

2、什么是并發(fā)?

并發(fā)性和并行性經(jīng)?;鞛橐徽?。為了更好地理解并發(fā)和并行之間的區(qū)別,讓我們看一個(gè)現(xiàn)實(shí)世界中的并發(fā)示例。

如果我們以一家餐館為例,那么就有幾組不同的工作類型(或可復(fù)制的程序)發(fā)生在一家餐館中。

  • 主管(負(fù)責(zé)安排客人入座)
  • 服務(wù)員(負(fù)責(zé)接單和提供食物)
  • 廚房(負(fù)責(zé)烹飪食物)
  • Bussers(負(fù)責(zé)清理桌子)
  • 洗碗機(jī)(負(fù)責(zé)清理餐具)

這些小組中的每一個(gè)都負(fù)責(zé)不同的任務(wù),所有這些最終都會(huì)導(dǎo)致顧客吃到一頓飯,這稱為并發(fā)。 專門的工作中心可以專注于單個(gè)任務(wù),這些任務(wù)結(jié)合起來(lái)會(huì)產(chǎn)生結(jié)果。

如果餐廳每項(xiàng)任務(wù)只雇用一個(gè)人,餐廳的效率就會(huì)受到限制。這稱為序列化。如果餐廳只有一個(gè)服務(wù)員,那么一次只能接受一個(gè)訂單。

并行性是處理并發(fā)任務(wù)并將它們分布在多個(gè)資源中的能力。在餐廳,這將包括服務(wù)員、食物準(zhǔn)備和清潔。如果有多個(gè)服務(wù)器,則可以一次接受多個(gè)訂單。

每個(gè)小組都能夠?qū)W⒂谒麄兊奶囟üぷ髦行?,而不必?fù)?dān)心上下文切換、最大化吞吐量或最小化延遲。

具有并行工作中心的行業(yè)的其他示例包括工廠工人和裝配線工人。本質(zhì)上,任何可以分解為更小的可重復(fù)任務(wù)的過(guò)程都可以被認(rèn)為是并發(fā)的,因此在使用適當(dāng)?shù)牟l(fā)設(shè)計(jì)時(shí)可以并行化。

TL;DR: 并發(fā)可以實(shí)現(xiàn)正確的并行性,但并行代碼不需要并行性。

原文鏈接:https://benjiv.com/parallelism-vs-concurrency/

本文轉(zhuǎn)載自微信公眾號(hào)「幽鬼」,可以通過(guò)以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系幽鬼公眾號(hào)。

 

責(zé)任編輯:武曉燕 來(lái)源: 幽鬼
相關(guān)推薦

2023-12-29 08:10:41

Go并發(fā)開發(fā)

2014-05-20 16:27:35

JVMScala

2024-02-19 00:00:00

JavaScriptJavaPython

2013-07-17 17:03:23

Ngx_luaNginx

2022-09-23 10:25:00

VueReact

2010-04-21 09:01:48

MySQL

2022-05-18 10:26:01

藍(lán)牙WiFi

2024-12-16 17:00:00

并行并發(fā)Java

2021-10-18 10:17:07

Go Golang語(yǔ)言

2024-07-08 00:01:00

GPM模型調(diào)度器

2025-02-26 03:00:00

2024-12-26 09:15:28

2023-11-12 17:19:07

并行并發(fā)場(chǎng)景

2012-11-15 10:18:11

IBMdw

2019-12-25 09:49:12

WebKitWindowsChrome

2009-08-04 14:48:26

并發(fā)和并行的區(qū)別

2014-06-12 19:38:29

LinuxBSD

2017-03-23 18:24:41

CentOSUbuntuLinux

2024-08-26 09:51:57

2015-01-19 13:33:44

KubernetesMesos計(jì)算集群
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)