介绍 (Introduction)
Not so long ago, I was asked to take a look at a service which was intermittently generating exceptions on stop and shutdown operations.
不久前,我被要求看一下一项服务,该服务间歇性地在停止和关闭操作上生成异常。
The service itself was a simple device management component, using a task to connect to a remote device when it had booted up, doing some updates and that was sort of it. But the target device was not always ready when the connection attempt was made, so a while
loop was used with a pause injected in the form of a call to Thread.Sleep(20000)
.
该服务本身是一个简单的设备管理组件,它使用一个任务在启动时连接到远程设备,进行一些更新,仅此而已。 但是,当尝试进行连接时,目标设备并不总是准备就绪,因此使用了while
循环,并以对Thread.Sleep(20000)
的调用的形式注入了暂停。
The idea of pausing a task for 20 seconds odd, before trying again makes perfect sense - but the 20 second thread block would intermittently, dependent on timing, cause the service manager to forcibly kill off the service due to shutdown or stop timeouts being triggered, and resulting exceptions would fill the logs.
在再次尝试之前将任务暂停20秒的想法非常合理-但是20秒线程块会间歇性地(取决于时间),导致服务管理器由于关机或触发触发触发而强行终止服务,并且产生的异常将填充日志。
Thread.Sleep(n)
cannot be cancelled - instead, consider using the CancellationToken.WaitHandle.WaitOne(n)
.
不能取消Thread.Sleep(n)
-相反,请考虑使用CancellationToken.WaitHandle.WaitOne(n)
。
使用代码 (Using the Code)
The code in this tip is a little sample of how Thread.Sleep
and CancellationToken.WaitHandle.WaitOne
behave in tasks, that you can experiment with.
本技巧文章中的代码是Thread.Sleep
和CancellationToken.WaitHandle.WaitOne
在任务中的行为的一些示例,您可以尝试使用它们。
The original while
loop implemented in the service looked something like this:
在服务中实现的原始while
循环如下所示:
while (!cancellationToken.IsCancellationRequested)
{
// Processing
if(connectionReady)
{
// Do its business
break;
}
// Pause for 20 seconds before trying again
Thread.Sleep(20000);
}
In this implementation, the thread will be blocked for 20 seconds - irrespective of any cancellation triggers. This was the underlying problem - dependent on the timing of the stop operation in relation to the Thread.Sleep
call, the service manager would timeout the stop operation and forcibly kill the management service.
在此实现中,线程将被阻塞20秒-不管是否有任何取消触发器。 这是潜在的问题-根据与Thread.Sleep
调用相关的停止操作的时间,服务管理器将使停止操作超时并强行终止管理服务。
Pausing code execution, but being cancellation request aware is simple enough:
暂停执行代码,但知道取消请求很简单:
while (!cancellationToken.IsCancellationRequested)
{
// Processing
if(connectionReady)
{
// Do its business
break;
}
// Pause for 20 seconds before trying again - now with cancellation support
var cancellationTriggered = cancellationToken.WaitHandle.WaitOne(20000);
}
This one line change achieves the same 20 second pause that the original implementation did, and it is also task cancellation aware.
这一行更改实现了与原始实现相同的20秒暂停,并且它也知道任务取消。
兴趣点 (Points of Interest)
If you find Thread.Sleep(n)
used around the place, consider switching to using the CancellationToken.WaitHandle.WaitOne(n)
method instead. It will help in keeping the exceptions log down to size.
如果在该位置周围发现Thread.Sleep(n)
,请考虑改用CancellationToken.WaitHandle.WaitOne(n)
方法。 这将有助于保持异常记录的大小。
翻译自: https://www.codeproject.com/Tips/5267935/Use-CancellationToken-not-Thread-Sleep