随机顺序的英文序翻译序英语怎么说-男生美白
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在线翻译读音
发布评论