分布式网络的作业写了三整天,今天晚上终于把扩展实验之前的部分的代码给调试通过了,这次应该是没有什么问题了。在调试这些分布式代码时遇到几个坑,记录一下下次写代码的时候要注意一下。(主要都是多线程环境下原子操作的一些坑)
1. 对同一变量在一个函数里的不同位置访问时得到的值可能不同 2. 对某一系统状态,在需要使用该状态时需要多次确认状态的值,否则容易产生错误。
具体的案例分析如下:
原子操作的注意事项
package main import "fmt" type A struct { value int } func (a *A) demo() bool { value1 := a.value var value2 int for i := range []int{1,2,3,4,5}{ value2 = i } value2 = a.value return value1 == value2 } func main() { count := 0 demo_a := A{} for demo_a.demo(){ demo_a.value = 1 go func(a *A){ a.value = 100 }(&demo_a) demo_a.demo() count++ } fmt.Printf("%v\n",count) } // Result // [kunwu@localhost atomic]$ go run atomic.go // 1456
由于协程的存在,上面的demo函数在运行时会出现value1和value2不同的情况,这种情况下看不出什么问题,但是如果是下面的情况就会出现问题,循环的次数就会和预期不同。而且这种情况由于两段代码离得比较远,还没法用锁。
msg.a := a.value // // Some code // for index:=0; index<a.value; index++ { // // Some code // }我的想法是如果在函数里需要多次访问同一变量的化,最好在程序开始时就给把值用零时变量存起来,这样在使用的时候就不会有问题。
还有一个小地方
// goroutine1 1. i++ 2. array[i] = value // goroutinue2 1. a = array[i]在协程的环境下,若先执行goroutine1的1,之后执行goroutine2的1,之后再执行goroutine2的2,就会取出未赋值的值。这里可以使用加锁的方法来解决,不过还是c++里的办法比较好
array[i++] = value
系统状态的确认
这个坑比较好理解,我就直接放伪代码了,原本的想法是启动一个定时器,等peer的回复时不断执行事件,在得到一定的回复时取消事件执行,但是在启动定时器的地方没有加状态判断,导致有些情况下先进行了Stop timer的操作,后才执行Start timer的操作,导致timer持续执行。我的解决办法是在Start timer的地方加上状态判断,比如这里可以加上a.count > B的判断。
var A classA func request() { for i in range(peers){ go func(a classA) { if ok := sendRequest(); ok == true{ a.count++ if a.count > B { // Stop the timer } } }(a) } // Start the timer }