【发布时间】:2017-11-14 11:38:45
【问题描述】:
所以我有一个非常奇怪的行为,c# 任务延迟有点让我发疯。
上下文: 我正在使用 C# .net 通过 R4852 与我们的一台设备进行通信。设备完成每个命令大约需要 200 毫秒,因此我在通信类中引入了 250 毫秒的延迟。
错误/不良行为:我的通信类中的延迟有时会等待 250 毫秒,有时只等待 125 毫秒。这是可重现的,并且当我增加延迟时会发生相同的行为。例如。如果我将延迟设置为每秒 1000 毫秒,请求将只等待 875 毫秒,所以再次缺少 125 毫秒。 此行为仅在没有附加调试器的情况下发生,并且仅在某些机器上发生。我们生产部门将使用该软件的机器有这个问题,我现在正在使用的机器没有这个问题。两者都运行 Windows 10。
怎么时不时少了125ms? 我已经了解到 Task.Delay 方法使用的是精度为 15 毫秒的计时器。这并不能解释缺少的 125 毫秒,因为它最多应该延迟几毫秒而不是过早 125 毫秒。
以下方法是我用来将命令排队到我的设备的方法。有一个信号量负责,因此一次只能执行一个命令(_requestSemapohre),因此只能处理一个请求。
public async Task<bool> Request(WriteRequest request)
{
await _requestSemaphore.WaitAsync(); // block incoming calls
await Task.Delay(Delay); // delay
Write(_connectionIdDictionary[request.Connection], request.Request); // write
if (request is WriteReadRequest)
{
_currentRequest = request as WriteReadRequest;
var readSuccess = await _readSemaphore.WaitAsync(Timeout); // wait until read of line has finished
_currentRequest = null; // set _currentRequest to null
_requestSemaphore.Release(); // release next incoming call
if (!readSuccess)
{
return false;
}
else
{
return true;
}
}
else
{
if (request is WriteWithDelayRequest)
{
await Task.Delay((request as WriteWithDelayRequest).Delay);
}
_requestSemaphore.Release(); // release next incoming call
return true;
}
}
以下代码是将请求发送到上述方法的方法的一部分。我删除了一些行以保持简短。基本的东西(请求和等待)仍然存在
// this command is the first command and will always have a proper delay of 1000ms
var request = new Communication.Requests.WriteRequest(item.Connection, item.Command);
await _translator.Request(request);
// this request is the second request that is missing 125ms
var queryRequest = new Communication.Requests.WriteReadRequest(item.Connection, item.Query); // query that is being sent to check if the value has been sent properly
if (await _translator.Request(queryRequest)) // send the query to the device and wait for response
{
if (item.IsQueryValid(queryRequest.Response)) // check result
{
item.Success = true;
}
}
我发送给此方法的第一个请求是WriteRequest,第二个请求是WriteReadRequest。
我在使用名为 Device Monitoring Studio 的软件监视串行通信时查看串行端口通信时发现了这种行为。
这是实际串行通信的屏幕截图。在这种情况下,我使用了 1000 毫秒的延迟。您可以看到sens0002 命令在执行之前正好有 1 秒的延迟。下一个命令/查询sens?只有 875ms 的延迟。此屏幕截图是在未附加调试器时拍摄的。
这是另一个屏幕截图。延迟再次设置为 1000 毫秒,但这次附加了调试器。如您所见,第一个和第二个命令现在都有大约 1000 毫秒的延迟。
在以下两个屏幕截图中,您可以看到相同的行为,但延迟为 250 毫秒(已降至 125 毫秒)。第一个没有附加调试器的屏幕截图,第二个附加了调试器的屏幕截图。在第二个屏幕截图中,您还可以看到 35 毫秒的漂移很安静,但仍远不及之前丢失的 125 毫秒。
那我到底在看什么?快速而肮脏的解决方案是将延迟增加到 1000 毫秒,这样就不再是问题了,但我宁愿理解为什么会出现这个问题以及如何正确解决它。
干杯!
【问题讨论】:
-
愚蠢的问题 - 你为什么不
Thread.Sleep代替? -
@zaitsman 这样会阻塞主线程,不推荐
-
另外,不清楚是什么在调用您的
Request()方法,因此它可能与它有关 -
线程交换会导致这样的延迟——如果线程没有运行,计时器将不会触发。还解释了为什么
Thread.Sleep会导致更严重的偏差 -
@SouvikGhosh 不,如果整个事情都被包装成
Thread或Task.Run,它就不会了
标签: c# .net wpf asynchronous task