Rad*_*asa 0 c# multithreading asynchronous async-await
我有几百台设备,我需要每隔5秒检查一次它们的状态.
我正在使用的API包含一个阻塞函数,它调用一个dll并返回单个设备的状态
string status = ReadStatus(int deviceID); // waits here until the status is returned
Run Code Online (Sandbox Code Playgroud)
上面的函数通常会在几毫秒内返回状态,但是在某些情况下我可能无法恢复状态一秒或更长时间!或者更糟糕的是,一台设备可能根本没有响应.
因此,我需要引入一种异步性的形式,以确保一个没有响应的设备不会阻止所有其他被监控的设备.
我目前的做法如下
// triggers every 5 sec
public MonitorDevices_ElapsedInterval(object sender, ElapsedEventArgs elapsedEventArgs)
{
foreach (var device in lstDevices) // several hundred devices in the list
{
var task = device.ReadStatusAsync(device.ID, cts.Token);
tasks.Add(task);
}
// await all tasks finished, or timeout after 4900ms
await Task.WhenAny(Task.WhenAll(tasks), Task.Delay(4900, cts.Token));
cts.Cancel();
var devicesThatResponded = tasks.Where(t => t.Status == TaskStatus.RanToCompletion)
.Select(t => t.GetAwaiter().GetResult())
.ToList();
}
Run Code Online (Sandbox Code Playgroud)
以下是Device类
public async Task ReadStatusAsync(int deviceID, CancellationToken tk)
{
await Task.Delay(50, tk);
// calls the dll to return the status. Blocks until the status is return
Status = ReadStatus(deviceID);
}
Run Code Online (Sandbox Code Playgroud)
我的代码有几个问题
我认为这是一个巨大的潜在瓶颈.还有更好的方法吗?
这可能类似于Stephen Cleary在这里所评论的内容,但他没有提供替代使用Task.Delay()的成本是多少?
如果ReadStatus无法返回,我正在尝试使用取消令牌来取消那里等待响应的线程......这似乎不起作用.
await Task.Delay(50,tk)
Thread.Sleep(100000)//模拟设备没有响应
我仍然有大约20个工作线程活着(即使我期待cts.Cancel()杀死它们.
foreach循环同时激发几百个任务
因为ReadStatus
是同步的(我假设你不能改变它),并且因为每个人都需要独立,因为它们可以阻塞调用线程,所以你必须拥有数百个任务.这已经是最有效的方式了.
还有更好的方法吗?
如果每个设备应该读每5秒钟,然后有自己独立的定时器每个设备可能会更好.经过几个周期后,他们应该"均匀".
等待Task.Delay(50,tk);
我不建议使用Task.Delay
"trampoline"非异步代码.如果您希望在线程池上运行代码,只需将其包装在Task.Run
:
foreach (var device in lstDevices) // several hundred devices in the list
{
var task = Task.Run(() => device.ReadStatus(device.ID, cts.Token));
tasks.Add(task);
}
Run Code Online (Sandbox Code Playgroud)
我正在尝试使用取消令牌取消坐在那里等待响应的线程...这似乎不起作用.
取消令牌不会杀死线程.如果ReadStatus
观察其取消令牌,那么它应该取消; 如果没有,那么你无能为力.
线程池中的线程应该不会被终止; 当计时器下一次触发时,这会减少线程搅动.