Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/go/7.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
跨goroutine同步值(计数器)_Go_Channel_Goroutine - Fatal编程技术网

跨goroutine同步值(计数器)

跨goroutine同步值(计数器),go,channel,goroutine,Go,Channel,Goroutine,我有一个golang应用程序,它可以浏览网站的各个页面,并且应该下载网站上的每个链接。它看起来有点像这样,我事先不知道页数,所以这是同步完成的: 页码:=0 结果:=getPagepage c:=makechan*http.Response 对于大于0的结果{ 对于结果:=范围结果{ 转到myProxySwitcher.downloadChanresult.URL,c fmt.PrintlnmyProxySwitcher.counter } 页面++ 结果=getPagepage myProxy

我有一个golang应用程序,它可以浏览网站的各个页面,并且应该下载网站上的每个链接。它看起来有点像这样,我事先不知道页数,所以这是同步完成的:

页码:=0 结果:=getPagepage c:=makechan*http.Response 对于大于0的结果{ 对于结果:=范围结果{ 转到myProxySwitcher.downloadChanresult.URL,c fmt.PrintlnmyProxySwitcher.counter } 页面++ 结果=getPagepage myProxySwitcher.counter++ } 问题是,每10个请求,我就要更改用于连接网站的代理。为此,我创建了一个带有计数器成员的结构:

类型ProxySwitcher结构{ 代理[]字符串 客户端*http.client 计数器int } 然后,每次从downloadChan发出请求时,我都会增加计数器

func p*ProxySwitcher下载chanurl字符串,c chan*http.Response{ p、 柜台++ 代理:=p.proxies[intp.counter/10%lenp.proxies] res:=p.client.Geturl,代理
c代码中有一个竞争条件

在第一个代码段中,您正在修改主goroutine中的counter字段:

// ... myProxySwitcher.counter++ 在第三个代码段中,您还从另一个goroutine修改了该计数器:

// ... p、 柜台++ 这是Go中的非法代码。根据定义,结果是未定义的。要理解原因,您必须仔细阅读。提示:这可能不容易阅读

要修复它,您需要确保同步。有很多方法可以做到这一点

一种方法,正如在对您的问题的评论中所建议的那样,是使用互斥。下面是一个有点混乱的示例,因为它需要对主循环进行一些重构。但这是同步对计数器的访问的方法:

类型ProxySwitcher结构{ 代理[]字符串 客户端*http.client mu-sync.Mutex 计数器int } func p*ProxySwitcher下载chanurl字符串,c chan*http.Response{ p、 木锁 p、 柜台++ //拿着的时候要读p的 //下面是使用它的锁 计数器:=p计数器 p、 解锁 //这里您使用计数器而不是p计数器, //既然你不再拿锁了 代理:=p.proxies[intcounter/10%lenp.proxies] res:=p.client.Geturl,代理 c0{ 对于结果:=范围结果{ 转到myProxySwitcher.downloadChanresult.URL,c //这有点乱,需要重一点 //重构,但这应该可以解决竞争: myProxySwitcher.mu.Lock fmt.PrintlnmyProxySwitcher.counter myProxySwitcher.mu.Unlock } 页面++ 结果=getPagepage //同样…它很乱,需要重构 myProxySwitcher.mu.Lock myProxySwitcher.counter++ myProxySwitcher.mu.Unlock } 或者,您可以将该计数器更改为uint64,然后使用原子/同步软件包执行goroutine安全操作:

类型ProxySwitcher结构{ 代理[]字符串 客户端*http.client 计数器uint64 } func p*ProxySwitcher下载chanurl字符串,c chan*http.Response{ 计数器:=原子添加64&p计数器,1 //这里您使用计数器而不是p.counter,因为这是您的本地副本 代理:=p.proxies[intcounter/10%lenp.proxies] res:=p.client.Geturl,代理 c0{ 对于结果:=范围结果{ 转到myProxySwitcher.downloadChanresult.URL,c 计数器:=原子.LoadUint64和myProxySwitcher.counter 打印计数器 } 页面++ 结果=getPagepage 原子加法器64和myProxySwitcher.counter,1 }
我可能会使用上一个版本,因为它更干净,而且我们实际上不需要互斥体。

您已经创建了竞争条件。您需要使用互斥体或sync/atomic同步访问。
1
1
1
1
1
1
2
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
4
5
5
5
1
2
3
4
5
...