Go语言的并发
前言
Go是一種靜態強類型,``編譯型,并發型并且具有垃圾回收的編程語言
以上摘自維基百科Go語言的介紹.
從誕生之初,Go語言就備受矚目,Google的支持,強大的開發團隊,面向現代處理器的編程優化,一切都預示go語言的美好前景.
筆者的主力語言是Java,最近也抽空研究了一下Go語言,查到的資料中描述了很多Go的強大特性,這些特性都很誘人,但是還不足以稱為語言的基礎,本文將只闡述Go的并發特性.
Go語言的高并發(concurrency)
concurrency(并發) is not parallelism(并行)
Concurrency is about?dealing with?lots of things at once. Parallelism is about?doing?lots of things at once.
詳見Concurrency is not parallelism
Go的并發基礎: goroutine
A?goroutine?is a lightweight thread managed by the Go runtime.
goroutine是由Go runtime管理的比輕量級線程(LWP)更輕量的線程(姑且這么叫吧).它的開銷很小,可以輕易的同時啟動成千上萬個goroutine,設想一下Java中開啟10個線程的消耗在Go語言中可以用來開啟100甚至1000個goroutine,顯然Go語言的運行效率會更高,要明白goroutine到底是什么,讓我們先從線程開始講起
從線程開始
線程的基礎概念
線程分為三類kernel threads, user threads, and fibers
kernel threads 內核線程
kernel thread是由操作系統內核支持的線程,也是基本意義上的線程,kernel thread切換由內核完成.一般程序不會直接使用kernel thread,而是使用kernel thread的一種高級接口-輕量級進程(Light Weight Process)一個LWP對應于一個kernel thread.
user threads 用戶線程
user thread指不需要內核支持而在用戶程序中實現的線程,它不依賴于內核,用戶自己實現user thread的創建,同步,調度,管理等功能.這里一個LWP對應多個user thread,簡單點說 一個進程擁有多個LWP,一個LWP又擁有多個user thread.,這里的用戶一般指的是編程語言
fiber 混合
兩者的混合,折中方案,不多說
它們的優缺點都是什么
對于kernel thread和user thread,站在編程語言的角度來看,都有什么優缺點
kernel thread
kernel thread的優點:
kernel thread的缺點:
user thread
user thread的優點:
user thread的缺點:
綜上所述,kernel thread由內核控制,使用簡單但是效率不夠高.user thread由用戶(編程語言)實現,效率很高,但是實現起來比較復雜. 在講Go語言使用了哪種線程模式之前,先來看下Java
Java為什么從user thread轉為kernel thread?
JDK1.2時使用的還是user thread,Java的開發團隊稱之為Green Thread,為什么后面切換到了kernel thread呢? 查閱資料后在這里找到了原因,sco的說明 大致有一下兩點原因
還有以下說明
here is a significant processing overhead for the JVM to keep track of thread states and swap between them, so green thread mode has been deprecated and removed from more recent Java implementations
總體來看是Java開發團隊由于時代背景,user thread的實現有缺陷,因此才切換到kernel thread的線程模式
Go使用了哪個線程模型
Go使用了user thread的線程模型,在Go中稱之為goroutine,這是Go稱為并發型語言的基礎. Go語言中有邏輯處理器(Logic Processor)的概念,通過它完成user thread的調度.
下面看下goroutine如何解決user thread的缺陷
如何處理阻塞的user thread
下圖截取自 ,可以看到當goroutine執行了阻塞的系統調用后,調度器將這個LWP與LP分離,并創建一個新的LWP為這個LP提供服務
如何充分利用多核
參見Fork/Join中的工作竊取算法,TODO
再談內存模型
關于內存模型,參見共享內存和消息傳遞,總結一下
共享內存的通信是隱式的,線程之間通過寫-讀內存中的公共狀態來隱式通信,而線程之間的同步是顯式的,必須在代碼中寫好同步邏輯. 消息傳遞的通信是顯式的,線程之間通過明確的發送消息來通信,由于收消息肯定在發消息之后,因此線程之間的同步是隱式的
目前的語言大都共享內存,為什么不用消息傳遞?以前的編程語言大都使用了kernel thread模式,而在kernel thread之間傳遞消息消耗太大,因此只能選擇代碼編寫更麻煩的共享內存模型
Go使用了哪種內存模型
消息傳遞!,Go使用了user thread的線程模式.消息傳遞的消耗不再成為問題,自然要選擇更有利于并發的消息傳遞模型,在Go中,消息傳遞主要通過chan這個結構完成chan即通道.類似于Java nio中通道的概念.goroutine通過在chan中發送/讀取消息實現同步.
結語
本文簡要說明了Go語言高并發的實現基礎,可以看到Go的高并發是語言設計時天生的優勢.
- 命令式編程 vs 聲明式編程
- 線程-wikipedia
- 線程的實現
總結
- 上一篇: 通过生成器写一个日志调用方法
- 下一篇: Kubernetes 在宜信落地实践