摘要
多线程程序在处理公共变量的时候,要注意多线程之间可能存在的问题。在实际程序运行期间,由于各线程运行时间周期的存在差异,这可能导致不同的线程在读取相同的内存地址数据(公有变量数据)时,可能存在多个线程读取到相同的数值。特别是系统繁忙的时候,这种问题发生的概率将变高。
具体实例
下列函数在处理字典a的数据时,由于启动的多个线程都会在启动之后调用主线程的公共变量k,v。此时,由于主线运行速度相对比较慢,那么这会导致不同的线程程读到了相同的值。
func TestMultiThreadProblem(t *testing.T){
a:=make(map[int]int)
for i:=0;i<10;i++{
a[i]=100-rand.Int()
}
for k,v:=range a{
go func(){
time.Sleep(1*time.Microsecond)
log.Println(k,v)
}()
time.Sleep(1*time.Microsecond)
}
time.Sleep(10*time.Second)
}
-
执行结果如下
按照上面的结果,红框部分出现了两个线程读取到同一个值的情况。 -
原因分析
这是因为计算机线程的启动是无序启动,且主线程的执行相对比较慢,导致变量k,v变化的速度较慢。故在一个时间区间
tt
t
内,k,v的值被多个线程同时访问,出现了最终出现两个线程读取到同一个值的情况。 -
解决办法
在创建线程的同时,将所需数据直接作为参数传递的线程的函数中,线程运行过程中不再向共有变量读取数值,具体代码如下:
func TestMultiThreadProblem(t *testing.T){
a:=make(map[int]int)
for i:=0;i<10;i++{
a[i]=100-rand.Int()
}
for k,v:=range a{
go func(k1,v1 int){
time.Sleep(1*time.Microsecond)
log.Println(k1,v1)
}(k,v)
time.Sleep(1*time.Microsecond)
}
time.Sleep(10*time.Second)
}
结论
我们一定要注意线程在处理公共变量时可能存在的安全问题。系统在正常提供服务的时候这些问题很少会出现,但是如果系统处于繁忙状态,这些问题发生的概率会明显增高。