1. 首页 >> 焦点

Go 协程商场独霸的完成

Go 协程商场独霸

  • Select 梗阻编制
  • Context 编制

先说个场景:

假定营业中 A 处事需求调用 处事B ,协程请求设置 5s 商场 ,商场那么若何优雅完成 ?独霸的完

Select 商场独霸

思虑可否可以用 select + time.After 编制举办完成

这里次要独霸的是通道在携程之间通信的特点,当法度圭表类型调用成功后,协程会向通道中发送旗子记号。商场没调用成功前,独霸的完通道会梗阻。协程

select {  case res := <-c2:  fmt.Println(res) case <-time.After(time.Second * 3):  fmt.Println("timeout 2") }

当 c2 通道中罕有据时 ,商场并且超不时分没有抵达 3s  ,独霸的完走 case res := <-c2 这个营业逻辑,协程当超不时分抵达 3s ,商场 走的 case <-time.After(time.Second * 3) 这个营业逻辑 , 多么便可以完成商场 3s 的独霸的完独霸。

res:= <-c2 是协程因为channel 可以完成梗阻,那么 time.After 为啥可以梗阻呢 ?商场

看 After 源码。sleep.go 可以看到真实也是独霸的完 channel

func After(d Duration) <-chan Time {  return NewTimer(d).C}

无缺代码示例:

package timeoutimport ( "fmt" "testing" "time")func TestSelectTimeOut(t *testing.T) {  // 在这个例子中, 假定我们奉行了一个内部调用	, 2秒此后将下场写进c1 c1 := make(chan string, 1) go func() {   time.Sleep(time.Second * 2)  c1 <- "result 1" }() // 这里独霸select来完成商场
, `res := <-c1`等待通道下场	
, // `<- Time.After`则在等待1秒后前去一个值, 因为select起首 // 奉行那些不再梗阻的case, 所以这里会奉行商场法度圭表类型, 假定 // `res := <-c1`超出1秒没有奉行的话 select {  case res := <-c1:  fmt.Println(res) case <-time.After(time.Second * 1):  fmt.Println("timeout 1") } // 假定我们将超不时分设为3秒, 这个时辰`res := <-c2`将在 // 商场case之前奉行, 从而可以输进写进通道c2的值 c2 := make(chan string, 1) go func() {   time.Sleep(time.Second * 2)  c2 <- "result 2" }() select {  case res := <-c2:  fmt.Println(res) case <-time.After(time.Second * 3):  fmt.Println("timeout 2") }}

运转下场:

=== RUN   TestSelectTimeOut
timeout 1
result 2
--- PASS: TestSelectTimeOut (3.00s)
PASS

go timer 计时器

这个是 timer 近似的计时器完成,通用也是经由过程通道来发送数据。

package mainimport "time"import "fmt"func main() {   // Ticker独霸和Timer近似的机制, 一样是独霸一个通道来发送数据。  // 这里我们独霸range函数来遍历通道数据, 这些数据每隔500毫秒被  // 发送一次
, 多么我们便可以领遭到  ticker := time.NewTicker(time.Millisecond * 500)  go func() {     for t := range ticker.C {     fmt.Println("Tick at", t)    }  }()  // Ticker和Timer一样可以被停止
。 一旦Ticker停止前, 通道将不再  // 领受数据	, 这里我们将在1500毫秒此后停止  time.Sleep(time.Millisecond * 1500)  ticker.Stop()  fmt.Println("Ticker stopped")}

go context

context 监听可否有 IO 独霸,最早从往后邻接中读取群集请求  ,每当读取到一个请求则会将该cancelCtx传进,用以传递消弭旗子记号,可发送消弭旗子记号,消弭全数举办中的群集请求 。

go func(ctx context.Context, info *Info) {    timeLimit := 120   timeoutCtx, cancel := context.WithTimeout(ctx, time.Duration(timeLimit)*time.Millisecond)   defer func() {     cancel()    wg.Done()   }()   resp := DoHttp(timeoutCtx, info.req)  }(ctx, info)

关头看营业代码: resp := DoHttp(timeoutCtx, info.req) 营业代码中包含 http 调用 NewRequestWithContext

req, err := http.NewRequestWithContext(ctx, "POST", url, strings.NewReader(paramString))

上面的代码 ,设置了过不时分,当DoHttp(timeoutCtx, info.req) 措置时分超出超不时分时,会主动阻拦,并且打印 context deadline exceeded。

看个代码:

package mainimport ( "context" "fmt" "testing" "time")func TestTimerContext(t *testing.T) {  now := time.Now() later, _ := time.ParseDuration("10s") ctx, cancel := context.WithDeadline(context.Background(), now.Add(later)) defer cancel() go Monitor(ctx) time.Sleep(20 * time.Second)}func Monitor(ctx context.Context) {  select {  case <-ctx.Done():  fmt.Println(ctx.Err()) case <-time.After(20 * time.Second):  fmt.Println("stop monitor") }}

运转下场 :

=== RUN   TestTimerContext
context deadline exceeded
--- PASS: TestTimerContext (20.00s)
PASS

Context 接口有以下 :

type Context inte***ce {     Deadline() (deadline time.Time, ok bool)    Done() <-chan struct{ }    Err() error    Value(key inte***ce{ }) inte***ce{ }}
  • Deadline — 前去 context.Context 被消弭的时分 ,也就是完成工作的阻拦日期;
  • Done — 前去一个 Channel,这个 Channel 会在往后工作完成或凹凸文被消弭此后封锁,多次调用 Done 编制会前去不合个 Channel;
  • Err — 前去 context.Context 停止的启事 ,它只会在 Done 前去的 Channel 被封锁时才会前去非空的值;
    • 假定 context.Context 被消弭 ,会前去 Canceled 偏向;
    • 假定 context.Context 商场 ,会前去 DeadlineExceeded 偏向;
  • Value — 从 context.Context 中掉落踪掉落踪键对应的值 ,对不合个凹凸文来讲  ,多次调用 Value 并传进不异的 Key 会前去不异的下场 ,该编制可以用来传递请求特定的数据;

到此这篇关于

Go 协程商场独霸

  • Select 梗阻编制
  • Context 编制

先说个场景:

假定营业中 A 处事需求调用 处事B  ,请求设置 5s 商场 ,那么若何优雅完成?

Select 商场独霸

思虑可否可以用 select + time.After 编制举办完成

这里次要独霸的是通道在携程之间通信的特点 ,当法度圭表类型调用成功后,会向通道中发送旗子记号。没调用成功前,通道会梗阻 。

select {  case res := <-c2:  fmt.Println(res) case <-time.After(time.Second * 3):  fmt.Println("timeout 2") }

当 c2 通道中罕有据时,并且超不时分没有抵达 3s ,走 case res := <-c2 这个营业逻辑 ,当超不时分抵达 3s , 走的 case <-time.After(time.Second * 3) 这个营业逻辑, 多么便可以完成商场 3s 的独霸 。

res:= <-c2 是因为channel 可以完成梗阻,那么 time.After 为啥可以梗阻呢?

看 After 源码。sleep.go 可以看到真实也是 channel

func After(d Duration) <-chan Time {  return NewTimer(d).C}

无缺代码示例:

package timeoutimport ( "fmt" "testing" "time")func TestSelectTimeOut(t *testing.T) {  // 在这个例子中, 假定我们奉行了一个内部调用, 2秒此后将下场写进c1 c1 := make(chan string, 1) go func() {   time.Sleep(time.Second * 2)  c1 <- "result 1" }() // 这里独霸select来完成商场
, `res := <-c1`等待通道下场, // `<- Time.After`则在等待1秒后前去一个值	, 因为select起首 // 奉行那些不再梗阻的case, 所以这里会奉行商场法度圭表类型, 假定 // `res := <-c1`超出1秒没有奉行的话 select {  case res := <-c1:  fmt.Println(res) case <-time.After(time.Second * 1):  fmt.Println("timeout 1") } // 假定我们将超不时分设为3秒, 这个时辰`res := <-c2`将在 // 商场case之前奉行, 从而可以输进写进通道c2的值 c2 := make(chan string, 1) go func() {   time.Sleep(time.Second * 2)  c2 <- "result 2" }() select {  case res := <-c2:  fmt.Println(res) case <-time.After(time.Second * 3):  fmt.Println("timeout 2") }}

运转下场 :

=== RUN   TestSelectTimeOut
timeout 1
result 2
--- PASS: TestSelectTimeOut (3.00s)
PASS

go timer 计时器

这个是 timer 近似的计时器完成 ,通用也是经由过程通道来发送数据。

package mainimport "time"import "fmt"func main() {   // Ticker独霸和Timer近似的机制, 一样是独霸一个通道来发送数据
。  // 这里我们独霸range函数来遍历通道数据, 这些数据每隔500毫秒被  // 发送一次, 多么我们便可以领遭到  ticker := time.NewTicker(time.Millisecond * 500)  go func() {     for t := range ticker.C {     fmt.Println("Tick at", t)    }  }()  // Ticker和Timer一样可以被停止
。 一旦Ticker停止前
, 通道将不再  // 领受数据, 这里我们将在1500毫秒此后停止  time.Sleep(time.Millisecond * 1500)  ticker.Stop()  fmt.Println("Ticker stopped")}

go context

context 监听可否有 IO 独霸,最早从往后邻接中读取群集请求  ,每当读取到一个请求则会将该cancelCtx传进,用以传递消弭旗子记号 ,可发送消弭旗子记号   ,消弭全数举办中的群集请求。

go func(ctx context.Context, info *Info) {    timeLimit := 120   timeoutCtx, cancel := context.WithTimeout(ctx, time.Duration(timeLimit)*time.Millisecond)   defer func() {     cancel()    wg.Done()   }()   resp := DoHttp(timeoutCtx, info.req)  }(ctx, info)

关头看营业代码: resp := DoHttp(timeoutCtx, info.req) 营业代码中包含 http 调用 NewRequestWithContext

req, err := http.NewRequestWithContext(ctx, "POST", url, strings.NewReader(paramString))

上面的代码,设置了过不时分 ,当DoHttp(timeoutCtx, info.req) 措置时分超出超不时分时 ,会主动阻拦,并且打印 context deadline exceeded。

看个代码 :

package mainimport ( "context" "fmt" "testing" "time")func TestTimerContext(t *testing.T) {  now := time.Now() later, _ := time.ParseDuration("10s") ctx, cancel := context.WithDeadline(context.Background(), now.Add(later)) defer cancel() go Monitor(ctx) time.Sleep(20 * time.Second)}func Monitor(ctx context.Context) {  select {  case <-ctx.Done():  fmt.Println(ctx.Err()) case <-time.After(20 * time.Second):  fmt.Println("stop monitor") }}

运转下场:

=== RUN   TestTimerContext
context deadline exceeded
--- PASS: TestTimerContext (20.00s)
PASS

Context 接口有以下:

type Context inte***ce {     Deadline() (deadline time.Time, ok bool)    Done() <-chan struct{ }    Err() error    Value(key inte***ce{ }) inte***ce{ }}
  • Deadline — 前去 context.Context 被消弭的时分,也就是完成工作的阻拦日期;
  • Done — 前去一个 Channel ,这个 Channel 会在往后工作完成或凹凸文被消弭此后封锁,多次调用 Done 编制会前去不合个 Channel;
  • Err — 前去 context.Context 停止的启事 ,它只会在 Done 前去的 Channel 被封锁时才会前去非空的值;
    • 假定 context.Context 被消弭 ,会前去 Canceled 偏向;
    • 假定 context.Context 商场,会前去 DeadlineExceeded 偏向;
  • Value — 从 context.Context 中掉落踪掉落踪键对应的值 ,对不合个凹凸文来讲 ,多次调用 Value 并传进不异的 Key 会前去不异的下场,该编制可以用来传递请求特定的数据;

到此这篇关于Go言语



版权声明:新流派 - 专业行业资讯与产品服务平台 - x6p.cn ,转载请注明出处:https://www.x6p.cn/news/43c23899718.html

 
QQ在线咨询
售前咨询热线
020-123456789
微信号
020-123456789