1. 问题描述
实际开发中遇见这样一个问题,在此记录一下:
Go多线程持续运行每3s读取一次数据过程中出现错误,此时多个线程同时报错(假设有3个线程),对该错误的处理方法被封装到了函数handleError()中,因此3个线程同时将错误抛给了handleError()。handleError()中3个线程同时处理同一个问题后,返回原来的函数重新执行。
此时有3个线程调用原来的函数。而这里我只希望有一个线程调用该函数。为什么只希望一个线程调用该函数呢?原因如下:
1)如果我是用handleError1()来处理错误,那么3个线程重新运行example函数后将结束运行,不能满足我持续运行读取数据的要求。
2)如果用handleError2()处理错误,那么3个线程,每个线程又调用3个子线程,加上主线程,将一共有9个线程重复读数据,不满足要求。
func main() {
for i := 0; i < 3; i++ {
go example(i)
}
}
func example (i int) {
// ...
if err != nil {
handleError(i)
}
}
func handleError1(i int) {
// ...
example(i)
}
func handleError2(i int) {
// ...
for i := 0; i < 3; i++ {
go example(i)
}
}
3)如果把for循环放到example中,多线程运行其中的匿名函数,如下所示,则会出现和上述2)一样的问题,出现9个线程。
func main() {
go example(i)
}
func example (i int) {
for i := 0; i < 3; i++ {
// 多线程运行匿名函数
go func (i int) {
// ...
if err != nil {
handleError(i)
}
}(i)
}
}
func handleError(i int) {
// ...
example(i)
}
2. 解决方案
这里设想了几种方案:
- 使用sync和额外的变量errNum=0,errNum记录所有线程抛出的错误数量。在报错时进行sync同步处理,处理第一个goroutine的错误前,errNum加1,判断errNum的值,大于1则return结束函数,否则执行函数handleError()。由于sync对线程同步,因此处理第二个线程的时候errNum=2,不满足条件,结束函数调用。这样就保证了只有一个线程调用了handleError()。
但是实际证明,该方法第一次有用,同一次运行时的第二次或第三次时,就会出现三个线程同时拿到errNum的初始值0的情况。原因不明,如果有知道的大佬,十分欢迎评论区或私信联系我,将不甚感激! - 加锁。当一个线程访问到handleError()时,立马给handleError()加锁,禁止其他线程访问。但是很明显,加锁后一个线程处理完,依然要放另一个线程进来处理。除非直接杀死另一个线程。那么不如在调用handleError()之前就杀死多余进程。
3. 代码实现
这里for循环放在函数example()内外的两种方式都可以,不影响功能。这里只用第一种方式举例实现。
注意:保证goroutine数量为2的时候调用handleError(),1个goroutine是主进程,1个goroutine是那3个子进程中的1个,负责调用handleError()。
func main() {
for i := 0; i < 3; i++ {
go example(i)
}
}
func example (i int) {
// ...
if err != nil {
for runtime.NumGoroutine() != 2 {
runtime.Goexit()
}
handleError(i)
}
}
func handleError1(i int) {
// ...
example(i)
}
func handleError2(i int) {
// ...
for i := 0; i < 3; i++ {
go example(i)
}
}
转载:https://blog.csdn.net/weixin_36465540/article/details/115719164
查看评论