什么是并发错误
并发是指程序能够交替执行不同的任务,以达到"同时执行效果",加快程序的运行效率。
但是并发也会导致一系列问题,以变量+1赋值为例,由于操作实际由多条指令组成,不同任务执行指令的顺序可能是交错的,所以就可能出现执行结果与我们预期结果不符合的情况。
并发错误很难捕获。这是因为触发并发错误需要线程以特定的顺序并行执行指令,以使程序显示错误行为。此外,触发并发错误具有不确定性。即使在触发并发错误之后,由于非确定性,将其重现并调试也可能很困难。
如果您是.NET开发者,可以尝试使用Coyote来自动检测并发错误。
Coyote
Coyote是由微软研究院提供的一个.NET库,旨在帮助确保您的代码没有并发错误。
Coyote的核心是一个调度器,它在测试期间控制(通过二进制重写)程序的执行,并且能够系统地研究并发性和不确定性,以发现安全性和活跃性缺陷。
更为重要的是,一旦Coyote发现一个错误,一旦 Coyote 发现了一个错误,它就可以让您根据需要多次完全重现它,从而使调试和修复问题变得更加容易。
你可以运行下面的命令安装Coyote:
dotnet tool install --global Microsoft.Coyote.CLI
下面我们用官方示例程序https://github.com/microsoft/coyote-samples来体验一下Coyote的强大功能。
定位错误
首先,clone下代码后,运行下列命令进行编译示例程序代码:
powershell -f build.ps1
运行下列命令进行错误检查:
cd bin\net5.0\coyote rewrite BoundedBuffer.dllcoyote test BoundedBuffer.dll -m TestBoundedBufferMinimalDeadlock --iterations 100
测试完成后,将会提示发现错误,如下图:
您将得到一个解释所有这些的日志文件:
<ErrorLog> Deadlock detected. Task(0) is waiting for a task to complete,
but no other controlled tasks are enabled.
Task(1), Task(2) and Task(3) are waiting to acquire a resource that is already acquired,
but no other controlled tasks are enabled.
<StackTrace> at Microsoft.Coyote.Tasks.SynchronizedBlock.Mock.Wait()at BoundedBufferExample.BoundedBuffer.Take()
解决问题
通过错误日志的StackTrace,定位到出错代码:
while (this.Occupied == 0)
{Monitor.Wait(this.SyncObject);
}
然后看看释放SyncObject的代码:
Monitor.Pulse(this.SyncObject);
根据错误日志的ErrorLog,并对照官方文档的解释,只需要把Pulse
替换成PulseAll
即可解决问题:
结论
通过上面的示例,我们可以看到,未对代码做任何修改,就可以实现并发错误检测。
如果您对项目中的多线程代码不太放心,可以尝试使用Coyote来帮助检测,避免上线出现并发错误的可能性。
如果你觉得这篇文章对你有所启发,请关注我的个人公众号”My IO“,记住我!