当前位置:网站首页>Golang concurrency model
Golang concurrency model
2022-07-28 18:40:00 【Compose】
There are three classic ways to control concurrency , One is through channel Notification implements concurrency control One is WaitGroup, The other is Context.
1. Use the most basic pass channel Notification implements concurrency control
No buffer channel
Unbuffered channel means that the size of the channel is 0, in other words , This type of channel has no ability to save any value before receiving , It requires sending goroutine And receiving goroutine At the same time be ready to , Before the sending and receiving operations can be completed .
From the unbuffered channel definition above , send out goroutine And receiving gouroutine It has to be synchronized , At the same time, after preparation , If not ready at the same time , The first operation will block the wait , Until another corresponding operation is ready . This unbuffered channel is also called synchronous channel .
It is officially realized through unbuffered channels goroutine concurrency control
func main() {
ch := make(chan struct{})
go func() {
fmt.Println("do something..")
time.Sleep(time.Second * 1)
ch <- struct{}{}
}()
<-ch
fmt.Println("I am finished")
} When the Lord goroutine Run to the <-ch Accept channel When , If it's time to channel No data in , You'll be stuck waiting , Until it's worth . In this way, concurrency control can be simply realized
2. adopt sync In bag WaitGroup Implement concurrency control
stay sync In bag , Provides WaitGroup , It will wait for all it collects goroutine Task complete . stay WaitGroup There are three main methods in
- Add, Can be added or reduced goroutine The number of
- Done, amount to Add(-1)
- Wait, After execution, the main thread will be blocked , until WaitGroup The value in is reduced to 0
In the main goroutine in Add(delta int) Ask to wait goroutine The number of . In every one of them goroutine After completion Done() It means this goroutine Already completed , When all goroutine When it's all done , In the main goroutine in WaitGroup Back to back .
func main(){
var wg sync.WaitGroup
var urls = []string{
"http://www.golang.org/",
"http://www.google.com/",
"http://www.somestupidname.com/",
}
for _, url := range urls {
wg.Add(1)
go func(url string) {
defer wg.Done()
http.Get(url)
}(url)
}
wg.Wait()
}But in Golang In the official website , There's a phrase
- A WaitGroup must not be copied after first use.
Translation is enough , stay WaitGroup After the first use , Cannot be copied , Because there will be some problems
func main() {
wg := sync.WaitGroup{}
for i := 0; i < 5; i++ {
wg.Add(1)
go func(wg sync.WaitGroup, i int) {
log.Printf("i:%d", i)
wg.Done()
}(wg, i)
}
wg.Wait()
log.Println("exit")
}The operation results are as follows
2009/11/10 23:00:00 i:4
2009/11/10 23:00:00 i:0
2009/11/10 23:00:00 i:1
2009/11/10 23:00:00 i:2
2009/11/10 23:00:00 i:3
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [semacquire]:
sync.runtime_Semacquire(0x1040a13c, 0x44bc)
/usr/local/go/src/runtime/sema.go:47 +0x40
sync.(*WaitGroup).Wait(0x1040a130, 0x121460)
/usr/local/go/src/sync/waitgroup.go:131 +0x80
main.main()
/tmp/sandbox894380819/main.go:19 +0x120
It reminds me of all goroutine I've been sleeping , There's a deadlock . This is because wg Copy passed to goroutine in , Lead to only Add operation , Actually Done The operation is in wg A copy of is executed . therefore Wait It's deadlocked .
Correction method 1 :
Put anonymous functions inwgThe incoming type of is changed to*sync.WaitGrou, So you can quote the correct WaitGroup 了 .Correction method 2 :
Place in anonymous functionwgRemove the passed in parameters , because Go Support closure type , In anonymous functions, you can directly use the externalwgVariable
go There are five reference types in slice, channel, function, map, interface
interface yes Go One of the most successful designs in the language , Empty interface It can be regarded as “ The duck ” Type used , It makes the Go Such a static language has a certain dynamic , But it doesn't lose the advantage of compile time checking that static languages have in type safety . Rely on interfaces rather than implementations , Use combinations first, not inheritance , This is the basic principle of program abstraction . But for a long time C++ As a representative of the “ object-oriented ” Language distorts these principles , Let people go astray . Why bind methods and data ? Why should there be such abnormal design of multiple inheritance ? In object-oriented, the most important thing should be the message passing between objects , But why is it interpreted as encapsulated inheritance and polymorphism . Whether object-oriented is a reasonable way to realize program abstraction , Or because it exists, we think it is reasonable . Historical reasons , There are too many mistakes in the middle . Anyway ,Go Of interface Opened a new window for us .
3. stay Go 1.7 Later introduced powerful Context Context , Implement concurrency control
3.1 brief introduction
Use... In some simple scenarios channel and WaitGroup That's enough , But when faced with some complex and changeable network concurrency scenarios channel and WaitGroup It seems that I can't do what I want . Like a network request Request, Every Request You need to open one goroutine Do something , these goroutine It may open other goroutine, Such as databases and RPC service . So we need a way to track goroutine The plan , In order to control them , This is it. Go What language offers us Context, It's very appropriate to call it context , It is goroutine The context of . It is a running environment that includes a program 、 Live and snapshot, etc . Every program to run , You need to know the running state of the current program , Usually Go Encapsulate these in a Context in , Then pass it to the to be executed goroutine .
context Package is mainly used to deal with multiple goroutine Sharing data between , And multiple goroutine Management of .
3.2 package context
context The core of the package is struct Context, The interface declaration is as follows :
// A Context carries a deadline, cancelation signal, and request-scoped values
// across API boundaries. Its methods are safe for simultaneous use by multiple
// goroutines.
type Context interface {
// Done returns a channel that is closed when this `Context` is canceled
// or times out.
Done() <-chan struct{}
// Err indicates why this Context was canceled, after the Done channel
// is closed.
Err() error
// Deadline returns the time when this Context will be canceled, if any.
Deadline() (deadline time.Time, ok bool)
// Value returns the value associated with key or nil if none.
Value(key interface{}) interface{}
}Done()Returns a that can only accept datachanneltype , When it's time to context close perhaps Timeout time When it's time , The channel There will be one Cancel signalErr()stayDone()after , returncontextReason for cancellation .Deadline()Set thecontext cancelThe timing of theValue()Method allowsContextObjects carryrequestScope data , The data must be thread safe .
Context Object is thread safe , You can put one Context Object to any number of gorotuine, Execute on it Cancel In operation , all goroutine Will receive a cancellation signal .
One Context Can't have Cancel Method , At the same time, we can only Done channel receive data .
The reasons behind it are consistent : The function of receiving the cancellation signal and the function of transmitting the signal are usually not the same .
A typical scenario is : The parent operation starts as a child operation goroutine, The child operation cannot cancel the parent operation .
3.3 Inherit context
context Package provides some functions , Assist users from existing Context Object to create a new Context object .
these Context Objects form a tree : When one Context When the object is canceled , All inherited from it Context Will be canceled .
Background It's all Context The root of the object tree , It can't be cancelled . Its statement is as follows :
// Background returns an empty Context. It is never canceled, has no deadline,
// and has no values. Background is typically used in main, init, and tests,
// and as the top-level `Context` for incoming requests.
func Background() ContextWithCancel and WithTimeout function Will return inherited Context object , These objects can Than their father Context Cancel earlier .
When the request handler returns , The... Associated with the request Context Will be cancelled . When sending a request using multiple copies , have access to WithCancel Cancel redundant requests . WithTimeout It is very useful when setting the request timeout to the back-end server . Here are the declarations of these three functions :
// WithCancel returns a copy of parent whose Done channel is closed as soon as
// parent.Done is closed or cancel is called.
func WithCancel(parent Context) (ctx Context, cancel CancelFunc)
// A CancelFunc cancels a Context.
type CancelFunc func()
// WithTimeout returns a copy of parent whose Done channel is closed as soon as
// parent.Done is closed, cancel is called, or timeout elapses. The new
// Context's Deadline is the sooner of now+timeout and the parent's deadline, if
// any. If the timer is still running, the cancel function releases its
// resources.
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)
WithValue Function to connect the data of the request scope with Context Objects build relationships . The statement is as follows :
// WithValue returns a copy of parent whose Value method returns val for key.
func WithValue(parent Context, key interface{}, val interface{}) Context3.4 context Example
Of course , Want to know Context How does the package work , The best way is to look at an example .
package main
import (
"context"
"fmt"
"sync"
"time"
)
type Message struct {
netId int
Data string
}
type ServerConn struct {
sendCh chan Message
handleCh chan Message
wg *sync.WaitGroup
ctx context.Context
cancel context.CancelFunc
netId int
}
func main() {
conn := &ServerConn{
sendCh: make(chan Message),
handleCh: make(chan Message),
wg: &sync.WaitGroup{},
netId: 100,
}
conn.ctx, conn.cancel = context.WithCancel(context.WithValue(context.Background(), "key", conn.netId))
loopers := []func(*ServerConn, *sync.WaitGroup){readLoop, writeLoop, handleLoop}
for _, looper := range loopers {
conn.wg.Add(1)
go looper(conn, conn.wg)
}
go func() {
time.Sleep(time.Second * 3)
conn.cancel()
}()
conn.wg.Wait()
}
func readLoop(c *ServerConn, wg *sync.WaitGroup) {
netId, _ := c.ctx.Value("key").(int)
handlerCh := c.handleCh
ctx, _ := context.WithCancel(c.ctx)
cDone := ctx.Done()
defer wg.Done()
for {
time.Sleep(time.Second * 1)
select {
case <-cDone:
fmt.Println("readLoop close")
return
default:
handlerCh <- Message{netId, "Hello world"}
}
}
}
func handleLoop(c *ServerConn, wg *sync.WaitGroup) {
handlerCh := c.handleCh
sendCh := c.sendCh
ctx, _ := context.WithCancel(c.ctx)
cDone := ctx.Done()
defer wg.Done()
for {
select {
case handleData, ok := <-handlerCh:
if ok {
handleData.netId++
handleData.Data = "I am whole world"
sendCh <- handleData
}
case <-cDone:
fmt.Println("handleLoop close")
return
}
}
}
func writeLoop(c *ServerConn, wg *sync.WaitGroup) {
sendCh := c.sendCh
ctx, _ := context.WithCancel(c.ctx)
cDone := ctx.Done()
defer wg.Done()
for {
select {
case sendData, ok := <-sendCh:
if ok {
fmt.Println(sendData)
}
case <-cDone:
fmt.Println("writeLoop close")
return
}
}
}
In the example above ,� Imitated Golang The main business process of the background program , When one TCP When the connection arrives, start three goroutine To deal with sending and receiving and processing data respectively . And these three goroutine Is running concurrently , adopt channel、sync.WaitGroup and context Control data processing .
stay � Each cycle produces one goroutine, every last goroutine All of them are introduced context, At every goroutine Through ctx Create a Son Context, And through select Always monitor the Context Operating condition , When in the father Context When you quit , There is no � Obviously invoker Context Of Cancel function , But the analysis results , Son Context It was shut down correctly and reasonably , This is because , All based on this Context Or a derivative Context Will be notified , At this time, the cleaning operation can be carried out , Finally release goroutine, This is an elegant solution goroutine Uncontrollable problems after startup .
Here are the results :

3.5 Context Usage principle
- Don't put the
ContextPlace in structure , To pass as a parameter - With
ContextFunction method as parameter , Should put theContextAs the first parameter , Put it first . - Pass to a function method
ContextWhen , Do not pass nil, If you don't know what to deliver , Just usecontext.TODO ContextOfValueRelated methods should pass the necessary data , Don't use this transfer for all dataContextIt's thread safe , You can rest assured in more than onegoroutineIn the transfer
边栏推荐
- Bubble sorting and Related videos
- 连线:谁拥有未来的艺术?OpenAI允许Dall-E用户将作品商用化,目前而言
- MySQL advanced mvcc (ultra detailed collation)
- NPM cannot recognize the "NPM" item as the name of a cmdlet, function, script file, or runnable program. Please check the spelling of the name. If the path is included, make sure the path is correct,
- Summer Challenge [FFH] JS custom component: DIY a keyboard that can be used at any time! (I)
- 初识结构体
- Shenzhen offline registration starrocks on AWS: how to conduct rapid unified analysis of real-time data warehouses
- Leetcode79 method 1: deep search
- MongoDB数据库shell命令执行
- First understanding of structure
猜你喜欢

Random talk on GIS data (VI) - projection coordinate system

微信公众号授权登录后报redirect_uri参数错误的问题

2022-07-27 第四小组 修身课 学习笔记(every day)

NPM cannot recognize the "NPM" item as the name of a cmdlet, function, script file, or runnable program. Please check the spelling of the name. If the path is included, make sure the path is correct,

Ue5 gas learning notes 0.2 configuration plug-in

Composition and principle of vector network analyzer (vector network)

npm 无法将“npm”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。请检查名称的拼写,如果包括路径,请确保路径正确,然后再试一次。

Six countries in Europe and the United States launched an express line product to optimize the "end-to-end" performance service on the 5th

@Autowired与@Resource区别

Error 2003 (HY000) can't connect to MySQL server on 'localhost3306' (10061) solution
随机推荐
Ue5 gas learning notes 1.1 capability system component
微信公众号授权登录后报redirect_uri参数错误的问题
Multithreading and high concurrency -- source code analysis AQS principle
MongoDB初始化
Ue5 gas learning notes 0.1 case Preview
Is it really realistic that people who have not been exposed to software testing can take up their posts after two months of training?
Novice record: some small knowledge of mechanical keyboard
数字化洪流 :企业转型中的资源重组与战略冲突
solidity的msg.value
Error 2003 (HY000) can't connect to MySQL server on 'localhost3306' (10061) solution
Introduction to the principle of signal source
Go的sleep
Software testing needs more and more talents, but fewer people are on the road of testing?
VSC上写Go出现expected ‘package‘, found ‘EOF‘
记录自己在厦门两年来的面试经历--完结篇
当Golang遇到高并发秒杀
Look at Devops construction from SRE
Ue5 gas learning notes 1.7 task ability tasks
UE5 GAS 学习笔记0.2配置插件
Go's sleep