go_并發編程(1)

go并發編程

  • 一、 并發介紹
      • 1,進程和線程
      • 2,并發和并行
      • 3,協程和線程
      • 4,goroutine
  • 二、 Goroutine
    • 1,使用goroutine
      • 1)啟動單個goroutine
      • 2)啟動多個goroutine
    • 2,goroutine與線程
    • 3,goroutine調度
  • 三、runtime包
    • 1,runtime.Gosched()
    • 2,runtime.Goexit() 退出當時協程
    • 3, runtime.GOMAXPROCS
    • 4,將任務分配到不同的CPU邏輯核心上實現并行
    • 5,Go語言中的操作系統線程和goroutine的關系:
  • 四、channel
    • 1,CSP模型
    • 2,channel 類型
    • 3,創建channel
    • 4,channel 操作
      • 1)發送
      • 2)接收
      • 3)關閉
    • 5,無緩沖通道
    • 6,有緩沖通道
    • 7,從通道中遍歷獲取值
    • 8,單向通道
    • 8,通道總結

一、 并發介紹

1,進程和線程

  • A. 進程是程序在操作系統中的一次執行過程,系統進行資源分配和調度的一個獨立單位。
  • B. 線程是進程的一個執行實體,是CPU調度和分派的基本單位,它是比進程更小的能獨立運行的基本單位。
  • C.一個進程可以創建和撤銷多個線程;同一個進程中的多個線程之間可以并發執行

2,并發和并行

  • A. 多線程程序在一個核的cpu上運行,就是并發。
  • B. 多線程程序在多個核的cpu上運行,就是并行。

并發:
在這里插入圖片描述
并行:
在這里插入圖片描述

3,協程和線程

  • 協程:獨立的棧空間,共享堆空間,調度由用戶自己控制,本質上有點類似于用戶級線程,這些用戶級線程的調度也是自己實現的。
  • 線程:一個線程上可以跑多個協程,協程是輕量級的線程。

4,goroutine

  • goroutine 只是由官方實現的超級"線程池"。
    • 每個實力4~5KB的棧內存占用和由于實現機制而大幅減少的創建和銷毀開銷是go高并發的根本原因
  • goroutine 奉行通過通信來共享內存,而不是共享內存來通信。

二、 Goroutine

Go語言引入了goroutine機制,簡化了并發編程。程序員只需定義任務函數,通過開啟goroutine實現并發執行,而無需自己管理線程池、任務調度和上下文切換。
Go運行時負責智能分配任務到CPU,將復雜性隱藏在底層。
這使得Go成為現代化編程語言,使并發編程更加簡單和高效。

1,使用goroutine

  • 在函數,或匿名函數 前面添加 go 關鍵詞。
  • 一個goroutine必定對應一個函數,可以創建多個goroutine去執行相同的函數。

1)啟動單個goroutine

func hello() {fmt.Println("Hello Goroutine!")
}
func main() {go  hello()fmt.Println("main goroutine done!")
}//但打印沒有 ,Hello Goroutine! ,因為main 生命周期結束,goroutine 還沒啟動//讓main 等等 goroutine 粗暴方法:func main() {go hello() // 啟動另外一個goroutine去執行hello函數fmt.Println("main goroutine done!")time.Sleep(time.Second)
}

2)啟動多個goroutine

  • 使用了sync.WaitGroup來實現goroutine的同步
var wg sync.WaitGroupfunc hello(i int) {defer wg.Done() // goroutine結束就登記-1fmt.Println("Hello Goroutine!", i)
}
func main() {for i := 0; i < 10; i++ {wg.Add(1) // 啟動一個goroutine就登記+1go hello(i)}wg.Wait() // 等待所有登記的goroutine都結束
}打印出來,并不是順序,因為這是因為10個goroutine是并發執行的,而goroutine的調度是隨機的。

2,goroutine與線程

  • 可增長棧
    OS線程(操作系統線程)一般都有固定的棧內存(通常為2MB),一個goroutine的棧在其生命周期開始時只有很小的棧(典型情況下2KB),goroutine的棧不是固定的,他可以按需增大和縮小,goroutine的棧大小限制可以達到1GB,雖然極少會用到這個大。所以在Go語言中一次創建十萬左右的goroutine也是可以的。

3,goroutine調度

Go語言的運行時(runtime)引入了GPM模型來實現并發調度,與傳統操作系統調度不同。

  • G(goroutine): G是goroutine的縮寫,代表一個任務單元。它存儲了該任務的信息,以及與所在P(處理器)的關聯。

  • P(處理器): P管理一組goroutine隊列,包含當前goroutine的運行上下文。P負責調度自己的隊列,比如暫停耗時長的任務、切換到其他任務。當P隊列為空,它會從全局隊列取任務,甚至從其他P隊列搶占任務。

  • M(機器): M是Go運行時對操作系統內核線程的虛擬。通常是一一對應的關系,每個M執行一個goroutine。當一個G長時間阻塞在一個M上,會創建新的M,將其他G掛載在新M上。舊M釋放后,用于回收資源。

  • GOMAXPROCS: 用于設定P的個數,控制并發度,但不會過多地增加P和M,以避免頻繁切換的開銷。

Go語言與其他語言不同之處在于,它在運行時實現了自己的調度器,使用m:n調度技術。這意味著goroutine的調度發生在用戶態,避免了內核態與用戶態的頻繁切換,包括內存分配與釋放都在用戶態維護,性能開銷較小。此外,Go語言充分利用多核硬件資源,將多個goroutine均勻分配在物理線程上,加上goroutine的輕量特性,保證了高效的并發調度性能。

三、runtime包

1,runtime.Gosched()

一種協作式多任務切換的方式,讓正在運行的 goroutine 暫時停下來,讓其他等待執行的 goroutine 有機會運行。

package mainimport ("fmt""runtime"
)func main() {go func(s string) {for i := 0; i < 5; i++ {fmt.Println(s)}}("world")// 主程for i := 0; i < 2; i++ {//切換 再次分配任務runtime.Gosched()fmt.Println("hello")}
}

2,runtime.Goexit() 退出當時協程

package mainimport ("fmt""runtime"
)func main() {go func() {defer fmt.Println("A.defer")func() {defer fmt.Println("B.defer")// 結束協程runtime.Goexit()defer fmt.Println("C.defer")fmt.Println("B")}()fmt.Println("A")}()for {}
}

3, runtime.GOMAXPROCS

  • Go運行時的調度器使用GOMAXPROCS參數來確定需要使用多少個OS線程來同時執行Go代碼。默認值是機器上的CPU核心數。例如在一個8核心的機器上,調度器會把Go代碼同時調度到8個OS線程上(GOMAXPROCS是m:n調度中的n)。

  • Go語言中可以通過runtime.GOMAXPROCS()函數設置當前程序并發時占用的CPU邏輯核心數。

  • Go1.5版本之前,默認使用的是單核心執行。Go1.5版本之后,默認使用全部的CPU邏輯核心數。

4,將任務分配到不同的CPU邏輯核心上實現并行

  • 單核心
package mainimport ("fmt""runtime""time"
)func a() {for i := 1; i < 10; i++ {fmt.Println("A:", i)}
}func b() {for i := 1; i < 10; i++ {fmt.Println("B:", i)}
}func main() {runtime.GOMAXPROCS(1)go a()go b()time.Sleep(time.Second)
}輸出,看出是執行完一個goroutine ,再執行另一個
B: 1
B: 2
B: 3
B: 4
B: 5
B: 6
B: 7
B: 8
B: 9
A: 1
A: 2
A: 3
A: 4
A: 5
A: 6
A: 7
A: 8
A: 9
  • 多核心
package mainimport ("fmt""runtime""time"
)func a() {for i := 1; i < 10; i++ {fmt.Println("A:", i)}
}func b() {for i := 1; i < 10; i++ {fmt.Println("B:", i)}
}func main() {runtime.GOMAXPROCS(2)go a()go b()time.Sleep(time.Second)
}輸出,看出是并發執行
B: 1
B: 2
B: 3
B: 4
B: 5
B: 6
B: 7
B: 8
A: 1
A: 2
A: 3
A: 4
A: 5
A: 6
A: 7
A: 8
A: 9
B: 9

5,Go語言中的操作系統線程和goroutine的關系:

  • 1.一個操作系統線程對應用戶態多個goroutine。
  • 2.go程序可以同時使用多個操作系統線程。
  • 3.goroutine和OS線程是多對多的關系,即m:n

四、channel

1,CSP模型

并發執行函數的目的是讓多個任務同時進行,但僅僅并發執行函數是不夠的,因為這些函數可能需要相互交換數據。在并發環境中,共享內存雖然可以用于數據交換,但容易引發競態問題,而使用互斥量會影響性能。

Go語言采用了CSP(Communicating Sequential Processes)并發模型,強調通過通信來共享數據,而不是通過共享數據來進行通信。這種方式更加安全且高效。

  • 關鍵點:

    • CSP模型: Go語言采用了CSP模型,強調通過通信來實現協程(goroutine)間的數據交換,而不是直接共享內存。

    • 通道(channel): 通道是用于協程間通信的一種機制,類似于一個隊列,保證了數據的順序性。通過在通道中發送和接收數據,協程可以安全地進行交互。

    • 通道的特點: 每個通道都有特定的元素類型,通道的操作遵循先進先出原則。通過通道的發送和接收操作,協程之間可以安全地進行數據交換,避免了競態問題。

    • 并發優勢: 通過通道,Go語言實現了安全且高效的并發編程,允許協程在不同任務之間進行數據交換,而不需要顯式地使用互斥量進行加鎖。

通過使用通道,Go語言的并發模型強調了協程之間通過通信共享數據,而不是通過共享數據來進行通信,從而避免了許多傳統并發模型中常見的問題。這使得并發編程更加安全、簡潔和高效。

2,channel 類型

  • channel 是一種類型,引用類型
聲明格式:var 變量 chan 元素類型
例如:var ch1 chan int   // 聲明一個傳遞整型的通道var ch2 chan bool  // 聲明一個傳遞布爾型的通道var ch3 chan []int // 聲明一個傳遞int切片的通道

3,創建channel

通道是引用類型,通道類型的空值是nil。var ch chan int
fmt.Println(ch) // <nil>聲明的通道后需要使用make函數初始化之后才能使用。創建channel的格式如下:make(chan 元素類型, [緩沖大小])		// 緩沖大小可選例如:
ch4 := make(chan int)
ch5 := make(chan bool)
ch6 := make(chan []int)

4,channel 操作

1)發送

ch <- 10 // 把10發送到ch中

2)接收

x := <- ch // 從ch中接收值并賦值給變量x
<-ch       // 從ch中接收值,忽略結果

3)關閉

  close(ch)
  • 只有在通知接收方goroutine所有的數據都發送完畢的時候才需要關閉通道。
  • 通道是可以被垃圾回收機制回收的,它和關閉文件是不一樣的,在結束操作之后關閉文件是必須要做的,但關閉通道不是必須的。

注意:

	1.對一個關閉的通道再發送值就會導致panic。2.對一個關閉的通道進行接收會一直獲取值直到通道為空。3.對一個關閉的并且沒有值的通道執行接收操作會得到對應類型的零值。4.關閉一個已經關閉的通道會導致panic。

5,無緩沖通道

func main() {ch := make(chan int)ch <- 10fmt.Println("發送成功")
}//出現以下錯誤fatal error: all goroutines are asleep - deadlock!goroutine 1 [chan send]:main.main().../src/github.com/pprof/studygo/day06/channel02/main.go:8 +0x54
  • 無緩沖的通道必須有接收才能發送。
  • 上面的代碼會阻塞在ch <- 10這一行代碼形成死鎖

啟動一個goroutine 解決該問題:

func recv(c chan int) {ret := <-cfmt.Println("接收成功", ret)
}
func main() {ch := make(chan int)go recv(ch) // 啟用goroutine從通道接收值ch <- 10fmt.Println("發送成功")
}

無緩沖通道上的發送操作會阻塞,直到另一個goroutine在該通道上執行接收操作,這時值才能發送成功,兩個goroutine將繼續執行。相反,如果接收操作先執行,接收方的goroutine將阻塞,直到另一個goroutine在該通道上發送一個值。

使用無緩沖通道進行通信將導致發送和接收的goroutine同步化。因此,無緩沖通道也被稱為同步通道。

6,有緩沖通道

func main() {ch := make(chan int, 1) // 創建一個容量為1的有緩沖區通道ch <- 10fmt.Println("發送成功")
}
  • 只要通道的容量大于零,那么該通道就是有緩沖的通道,通道的容量表示通道中能存放元素的數量。
  • 可以使用內置的len函數獲取通道內元素的數量,使用cap函數獲取通道的容量。

7,從通道中遍歷獲取值

package mainimport "fmt"func main() {ch1 := make(chan int)ch2 := make(chan int)// 開啟goroutine 將 0~100 的數發到 ch1 中go func() {for i := 0; i < 100; i++ {ch1 <- i}close(ch1)}()// 開啟goroutine 從ch1中接收值,發送給ch2go func() {for {i, ok := <-ch1if !ok {break}ch2 <- i * i}close(ch2)}()// 在主goroutine 打印ch2for i := range ch2 {fmt.Println("ch2:", i)}
}
  • 能從關閉通道中獲取值
  • 通過遍歷獲取通道中(關閉的通道也行)的值

8,單向通道

func counter(out chan<- int) {for i := 0; i < 100; i++ {out <- i}close(out)
}func squarer(out chan<- int, in <-chan int) {for i := range in {out <- i * i}close(out)
}
func printer(in <-chan int) {for i := range in {fmt.Println(i)}
}func main() {ch1 := make(chan int)ch2 := make(chan int)go counter(ch1)go squarer(ch2, ch1)printer(ch2)
}
  • 1.chan<- int是一個只能發送的通道,可以發送但是不能接收;
  • 2.<-chan int是一個只能接收的通道,可以接收但是不能發送。

8,通道總結

在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/41343.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/41343.shtml
英文地址,請注明出處:http://en.pswp.cn/news/41343.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

在 React 中獲取數據的6種方法

一、前言 數據獲取是任何 react 應用程序的核心方面。對于 React 開發人員來說&#xff0c;了解不同的數據獲取方法以及哪些用例最適合他們很重要。 但首先&#xff0c;讓我們了解 JavaScript Promises。 簡而言之&#xff0c;promise 是一個 JavaScript 對象&#xff0c;它將…

Python Web:Django、Flask和FastAPI框架對比

原文&#xff1a;百度安全驗證 Django、Flask和FastAPI是Python Web框架中的三個主要代表。這些框架都有著各自的優點和缺點&#xff0c;適合不同類型和規模的應用程序。 1. Django&#xff1a; Django是一個全功能的Web框架&#xff0c;它提供了很多內置的應用程序和工具&am…

排序+運算>直接運算的效率的原因分析

大家好,我是愛編程的喵喵。雙985碩士畢業,現擔任全棧工程師一職,熱衷于將數據思維應用到工作與生活中。從事機器學習以及相關的前后端開發工作。曾在阿里云、科大訊飛、CCF等比賽獲得多次Top名次。現為CSDN博客專家、人工智能領域優質創作者。喜歡通過博客創作的方式對所學的…

ADIS16470和ADIS16500從到手到讀出完整數據,附例程

由于保密原因&#xff0c;不能上傳我這邊的代碼&#xff0c;我所用的開發環境是IAR&#xff0c; 下邊轉載別的博主的文章&#xff0c;他用的是MDK 下文的博主給了你一個很好的思路&#xff0c;特此提出表揚 最下方是我做的一些手冊批注&#xff0c;方便大家了解這個東西 原文鏈…

如何利用 ChatGPT 進行自動數據清理和預處理

推薦&#xff1a;使用 NSDT場景編輯器助你快速搭建可二次編輯的3D應用場景 ChatGPT 已經成為一把可用于多種應用的瑞士軍刀&#xff0c;并且有大量的空間將 ChatGPT 集成到數據科學工作流程中。 如果您曾經在真實數據集上訓練過機器學習模型&#xff0c;您就會知道數據清理和預…

有沒有比讀寫鎖更快的鎖

在之前的文章中&#xff0c;我們介紹了讀寫鎖&#xff0c;學習完之后你應該已經知道了讀寫鎖允許多個線程同時訪問共享變量&#xff0c;適用于讀多寫少的場景。那么在讀多寫少的場景中還有沒有更快的技術方案呢&#xff1f;還真有&#xff0c;在Java1.8這個版本里提供了一種叫S…

Docker安裝Skywalking APM分布式追蹤系統

Skywalking是一個應用性能管理(APM)系統&#xff0c;具有服務器性能監測&#xff0c;應用程序間調用關系及性能監測等功能&#xff0c;Skywalking分為服務端、管理界面、以及嵌入到程序中的探針部分&#xff0c;由程序中的探針采集各類調用數據發送給服務端保存&#xff0c;在管…

novnc 和 vnc server 如何實現通信?原理?

參考&#xff1a;https://www.codenong.com/js0f3b351a156c/

隨機微分方程

應用隨機過程|第7章 隨機微分方程 見知乎&#xff1a;https://zhuanlan.zhihu.com/p/348366892?utm_sourceqq&utm_mediumsocial&utm_oi1315073218793488384

復習3-5天【80天學習完《深入理解計算機系統》】第七天

專注 效率 記憶 預習 筆記 復習 做題 歡迎觀看我的博客&#xff0c;如有問題交流&#xff0c;歡迎評論區留言&#xff0c;一定盡快回復&#xff01;&#xff08;大家可以去看我的專欄&#xff0c;是所有文章的目錄&#xff09;   文章字體風格&#xff1a; 紅色文字表示&#…

Linux與bash(基礎內容一)

一、常見的linux命令&#xff1a; 1、文件&#xff1a; &#xff08;1&#xff09;常見的文件命令&#xff1a; &#xff08;2&#xff09;文件屬性&#xff1a; &#xff08;3&#xff09;修改文件屬性&#xff1a; 查看文件的屬性&#xff1a; ls -l 查看文件的屬性 ls …

神經網絡基礎-神經網絡補充概念-33-偏差與方差

概念 偏差&#xff08;Bias&#xff09;&#xff1a; 偏差是模型預測值與實際值之間的差距&#xff0c;它反映了模型對訓練數據的擬合能力。高偏差意味著模型無法很好地擬合訓練數據&#xff0c;通常會導致欠擬合。欠擬合是指模型過于簡單&#xff0c;不能捕捉數據中的復雜模式…

基于java在線讀書與分享論壇設計與實現

摘 要 互聯系統的技術在如今的社會中&#xff0c;應用的越來越廣泛&#xff0c;通過互聯系統我們可以更方便地進行辦公&#xff0c;也能夠在系統上就能處理很多日常的事務。互聯系統的技術的發展&#xff0c;也是人們日常中接觸更多的一項技術。隨著互聯系統的發展&#xff0c;…

Swin Transformer: Hierarchical Vision Transformer using Shifted Windows

Swin Transformer: Hierarchical Vision Transformer using Shifted Windows 摘要當前的檢測sota模型網絡架構swin Transformer和Vision Transformer的不同之處整體架構Patch Partition結構Linear Embedding結構Swin Transformer Block結構 Patch MergingW-MSAMSA模塊計算量W-M…

學習Vue:Vue3 VS Vue2

Vue 3作為Vue.js的最新版本&#xff0c;帶來了一系列令人激動的新特性和改進&#xff0c;讓開發者們在構建現代Web應用時體驗更加順暢和高效。本文將全面介紹Vue 3相對于Vue 2的改進&#xff0c;重點解釋Composition API的使用&#xff0c;以及新引入的Teleport和Suspense等特性…

Genoss GPT簡介:使用 Genoss 模型網關實現多個LLM模型的快速切換與集成

一、前言 生成式人工智能領域的發展繼續加速&#xff0c;大型語言模型 (LLM) 的用途范圍不斷擴大。這些用途跨越不同的領域&#xff0c;包括個人助理、文檔檢索以及圖像和文本生成。ChatGPT 等突破性應用程序為公司進入該領域并開始使用這項技術進行構建鋪平了道路。 大公司正…

如何發布自己的小程序

小程序的基礎內容組件 text&#xff1a; 文本支持長按選中的效果 <text selectable>151535313511</text> rich-text: 把HTML字符串渲染為對應的UI <rich-text nodes"<h1 stylecolor:red;>123</h1>"></rich-text> 小程序的…

MySql過濾重復數據

假設模型表是: 1. 根據單字段過濾: SELECT user_name, COUNT(*) as count FROM sys_user GROUP BY user_name HAVING count > 1;結果: 2. 根據多個字段查詢重復數據 SELECT user_name, email, COUNT(*) as count FROM sys_user GROUP BY user_name, email HAVING count…

[centos]設置主機名

1、設置 hostnamectl set-hostname 名字 2、查看是否生效 hostnamectl status 3、打開一個新鏈接就可以了

log4j教程_編程入門自學教程_菜鳥教程-免費教程分享

教程簡介 Log4j是Apache的一個開源項目&#xff0c;通過使用Log4j&#xff0c;我們可以控制日志信息輸送的目的地是控制臺、文件、GUI組件&#xff0c;甚至是套接口服務器、NT的事件記錄器、UNIX Syslog守護進程等&#xff1b;我們也可以控制每一條日志的輸出格式&#xff1b;…