Golang实践经验和教训(13)

default:

timeout.callback(timeout.session)

()

最后 , 这可以确保仅在遇到锁之后才执行回调 , 并且不会触发取消 。

死锁

此解决方案似乎有效;但是存在一个潜在的隐患——死锁 。

仔细检查代码 , 考虑并发调用的方法 。 问题在cancel通道本身 。 我们将其设置为无缓冲通道 , 这意味着其发送是阻塞调用 。 在超时处理程序中调用\"取消\"后 , 只有在该处理程序被取消后才能继续操作 。 这里的问题是 , 当有多个调用到同一取消通道时 , 取消请求仅使用一次 。 如果并发事件要取消相同的超时处理程序 , 例如链接断开或控制数据包事件 , 则很容易发生这种情况 。 这将导致死锁 , 可能会使应用程序停止 。

应对该死锁问题的解决方案是让通道缓冲一下 , 让发送并不总是阻塞 , 并且在并发调用的情况下显式使发送变为非阻塞 。 这样可以确保取消发送一次 , 并且不会阻止后续的取消调用 。

推荐阅读