随机顺序的英文序翻译序英语怎么说-男生美白


2023年4月7日发(作者:north korea)

如何优雅地关闭Gochannel

1.在不能更改channel状态的情况下,没有简单普遍的⽅式白首方悔读书迟的上一句是什么 来检查channel是否已经关闭了

2.关闭已经关闭的channel会导致panic,所以在closer(关闭者)不知道channel是否已经关闭的情况下去关闭channel是很危险的

3.发送值到已经关闭的channel会导致panic,所以如果sender(发送者)在不知道channel是否已经关闭的情况下去向channel发送值是很危险的

那些批评看起来都很有道理(实际上并没有)。是的,没有⼀个内置函数可以检查⼀个channel是否已经关闭。如果你能确定不会向channel发送

任何值,那么也确实需要⼀个简单的⽅法来检查channel是否已经关闭:

packagemain

import\"fmt\"

typeTint

funcIsClosed(ch<-chanT)bool{

select{

case<-ch:

returntrue

default:

}

returnfalse

}

funcmain(){

c:=make(chanT)

n(IsClosed(c))//false

close(c)

n(IsClosed(c))//true

}

上⾯已经提到了,没有⼀种适⽤的⽅式来检查channel是否已经关闭了。但是,就算有⼀个简单的

closed(chanT)bool

函数来检查channel是否已

经关闭,它的⽤处还是很有限的,就像内置的

len

函数⽤来检查缓冲channel中元素数量⼀样。原因就在于,已经检查过的channel的状态有可能

在调⽤了类似的⽅法返回之后就修改了,因此返回来的值已经不能够反映刚才检查的channel的当前状态了。

尽管在调⽤

closed(ch)

返回

true

的情况下停⽌向channel发送值是可以的,但是如果调⽤

closed(ch)

返回

false

,那么关闭channel或者继续向

channel发送值就不安全了(会panic)。

TheChannelClosingPrinciple

在使⽤Gochannel的时候,⼀个适⽤的原则是不要从接收端关闭channel,也不要关闭有多个并发发送者的channel。换句话说,如果

sender(发送者)只是唯⼀的sender或者是channel最后⼀个活跃的sender,那么你应该在sender的goroutine关闭channel,从⽽通知

receiver(s)(接收者们)已经没有值可以读了。维持这条原则将保证永远不会发⽣向⼀个已经关闭的channel发送值或者关闭⼀个已经关闭的

channel。

(下⾯,我们将会称上⾯的原则为channelclosingprinciple

打破channelclosingprinciple的解决⽅案

funcSafeSend(chchanT,valueT)(closedbool){

deferfunc(){

ifrecover()!=nil{

//thereturnresultcanbealtered

//inadeferfunctioncall

closed=true

}

}()

ch<-value//panicifchisclosed

returnfalse//<=>closed=false;return

}

如果channel

ch

没有被关闭的话,那么这个函数的性能将和

ch<-value

接近。对于channel关闭的时候,

SafeSend

函数只会在每个sender

goroutine中调⽤⼀次,因此程序不会有太⼤的性能损失。

同样的想法也可以⽤在从多个goroutine关闭channel中:

funcSafeClose(chchanT)(justClosedbool){

defer为而不争 func(){

ifrecover()!=nil{

justClo《性启示》未删在线播放 sed=false

}

}()

//assumech!=nilhere.

close(ch)//panicifchisclosed

returntrue

}

很多⼈喜欢⽤

来关闭channel:

typeMyChannelstruct{

CchanT

}

funcNewMyChannel()*MyChannel{

return&MyChannel{C:make(chanT)}

}

func(mc*MyChannel)SafeClose(){

(func(){

close(mc.C)

})

}

当然了,我们也可以⽤

来避免多次关闭channel:

typeMyChannelstruct{

CchanT

closedbool

}

funcNewMyChannel()*MyChannel{

return&MyChannel{C:make(chanT)}

}

func(mc*MyChannel)SafeClose(){

()

if!{

close(mc.C)

=true

}

()

}

func(mc*MyChannel)IsC温庭筠代表作 losed()bool{

()

()

}

我们应该要理解为什端午节祝福语 么Go不⽀持内置

SafeSend

SafeClose

函数,原因就在于并不推荐从接收端或者多个并发发送端关闭channel。Golang甚⾄

禁⽌关闭只接收(receive-only)的channel。

保持channelclosingprinciple的优雅⽅案

上⾯的

SaveSend

函数有⼀个缺点是,在select语句的

case

关键字后不能作为发送操作被调⽤(译者注:类似于

caseSafeSend(ch,t):

)。另外⼀

个缺点是,很多⼈,包括我⾃⼰都觉得上⾯通过使⽤

panic

/

recover

sync

包的⽅案不够优雅。针对各种场景,下⾯介绍不⽤使

panic

/

recover

sync

包,纯粹是利⽤channel的解决⽅案。

(在下⾯的例⼦总,

oup

只是⽤来让例⼦完整的。它的使⽤在实践中不⼀定⼀直都有⽤)

M个receivers,⼀个sender,sender通过关闭datachannel说“不再发送”

这是最简单的场景了,就只是当sender不想再发送的时候让sender关闭data来关闭channel:

packagemain

import(

\"time\"

\"math/rand\"

\"sync\"

\"log\"

)

funcmain(){

(().UnixNano())

gs(0)

//...

constMaxRandomNumber=100000

constNumReceivers=100

wgReceivers:=oup{}

(NumReceivers)

//...

dataCh:=make(chanint,100)

//thesender

gofunc(){

for{

if荒凉的近义词 value:=(MaxRandomNumber);value==0{

//theonlysendercanclosethechannelsafely.

close(dataCh)

return

}else{

dataCh<-value

}

}

}()

//receivers

fori:=0;i

gofunc(){

()

//receivevaluesuntildataChisclosedand

//thevaluebufferqueueofdataChisempty.

forvalue:=rangedataCh{

n(value)

}

}()

}

()

}

⼀个receiver,N个sender,receiver通过关闭⼀个额外的signalchannel说“请停⽌发送”

这种场景⽐上⼀个要复杂⼀点。我们不能让receiver关闭datachannel,因为这么做将会打破channelclosingprinciple。但是我们可以让

receiver关闭⼀个额外的signalchannel来通知sender停⽌发送值:

packagemain

import(

\"time\"

\"math/rand\"

\"sync\"

\"log\"

)

funcmain(){

(().UnixNano())

gs(0)

//...

constMaxRandomNumber=100000

constNumSenders=1000

wgReceivers:=oup{}

(1)

//...

dataCh:=make(chanint,100)

stopCh:=make(chanstruct{})

//stopChisanadditionalsignalchannel.

//ItssenderisthereceiverofchanneldataCh.

//ItsreveiversarethesendersofchanneldataCh.

//senders

fori:=0;i

gofunc(){

for{

value:=(MaxRandomNumber)

select{

case<-stopCh:

return

casedataCh<-value:

}

}

}()

}

//thereceiver

gofunc(){

()

forvalue:=rangedataCh{

ifvalue==MaxRandomNumber-1{

//thereceiverofthedataChchannelis

//alsothesenderofthestopChcahnnel.

//Itissafetoclosethestopchannelhere.

close(stopCh)

return

}

n(value)

}

}()

//...

()

}

正如注释说的,对于额外的signalchannel来说,它的sender是datachannel的receiver。这个额外的signalchannel被它唯⼀的sender关

闭,遵守了channelclosingprinciple。

M个receiver,N个sender,它们当中任意⼀个通过通知⼀个moderator(仲裁者)关闭额外的signalchannel来说“让我们结束游戏吧”

这是最复杂的场景了。我们不能让任意的receivers和senders关闭datachannel,也不能让任何⼀个receivers通过关闭⼀个额外的signal

channel来通知所有的senders和receivers退出游戏。这么做的话会打破channelclosingprinciple。但是,我们可以引⼊⼀个moderator

来关闭⼀个额外的signalchannel。这个例⼦的⼀个技巧是怎么通知moderator去关闭额外的signalchannel:

packagemain

import(

\"time\"

\"math/rand\"

\"sync\"

\"log\"

\"strconv\"

)

funcmain(){

(().UnixNano())

gs(0)

//...

constMaxRandomNumber=100000

constNumReceivers=10

constNumSenders=1000

wgReceivers:=oup{}

(NumReceivers)

//...

dataCh:=make(chanint,100)

stopCh:=make(chanstruct{})

//stopChisanadditionalsignalchannel.

//Itssenderisthemoderatorgoroutineshownbelow.

//ItsreveiversareallsendersandreceiversofdataCh.

toStop:=make(chanstring,1)

//thechanneltoStopisusedtonotifythemoderator

//tocloseth题西林壁拼音版古诗 eadditionalsignalchannel(stopCh).

//ItssendersareanysendersandreceiversofdataCh.

//Itsreveiveristhemoderatorgoroutineshownbelow.

varstoppedBystring

//moderator

gofunc(){

stoppedBy=<-toStop//partofthetrickusedtonotifythemoderator

//toclosetheadditionalsignalchannel.

close(stopCh)

}()

//senders

fori:=0;i

gofunc(idstring){

for{

value:=(MaxRandomNumber)

ifvalue==0{

//here,atr风雪夜归人什么意思 ickisusedtonotifythemoderator

//toclosetheadditionalsignalchannel.

select{

casetoStop<-\"sender#\"+id:

default:

}

return

}

//thefirstselecthereistotrytoexitthe

//goroutineasearlyaspossible.

select{

case<-stopCh:

re过年的祝福语 turn

default:

}

select{

case<-stopCh:

case<-stopCh:

return

casedataCh<-value:

}

}

}((i))

}

//receivers

fori:=0;i

gofunc(idstring){

()

for{

//sameassenders,thefirstselecthereisto

//trytoexitthegoroutineasearlyaspossible.

select{

case<-stopCh:

return

default:

}

select{

case<-stopCh:

return

casevalue:=<-dataCh:

ifvalue==MaxRandomNumber-1{

//thesametrickisusedtonotifythemoderator

//toclosetheadditionalsignalchannel.

select{

casetoStop<-\"receiver#\"+id:

default:

}

return

}

n(value)

}

}

}((i))

}

//...

()

n(\"stoppedby\",stoppedBy)

}

在这个例⼦中,仍然遵守着channelclosingprinciple。

请注意channel

toStop

的缓冲⼤⼩是1.这是为了避免当mederatorgoroutine准备好之前第⼀个通知就已经发送了,导致丢失。

更多的场景?

很多的场景变体是基于上⾯三种的。举个例⼦,⼀个基于最复杂情况的变体可能要求receivers读取bufferchannel中剩下所有的值。这应该

很容易处理,所有这篇⽂章也就不提了。

尽管上⾯三种场景不能覆盖所有Gochannel的使⽤场景,但它们是最基础的,实践中的⼤多数场景都可以分类到那三种中。

结论

这⾥没有⼀种场景要求你去打破channelclosingprinciple。如果你遇到了这种场景,请思考⼀下你的设计并重写你的代码。

⽤Go编程就像在创作艺术。

更多推荐

moderator是什么意思erator在线翻译读音