繁体   English   中英

高性能异步监控任务

[英]High performance async monitoring tasks

我有数百个设备,我需要每5秒检查一次它们的状态。

我正在使用的API包含一个阻止函数,该函数调用dll并返回单个设备的状态

string status = ReadStatus(int deviceID); // waits here until the status is returned

上面的函数通常在几毫秒内返回状态,但是在某些情况下,我可能一秒钟或更长时间都无法恢复状态! 甚至更糟的是,一台设备可能根本不响应。

因此,我需要引入一种异步形式,以确保一个无响应的设备不会阻止其他所有受监视的设备。

我目前的方法如下

// 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();
}

并在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);
}

我的代码有几个问题

  1. foreach循环同时触发数百个任务 ,其中Task.Callback的回调由线程池中的线程提供服务,每个任务花费几毫秒。

我认为这是一个巨大的潜在瓶颈。 有没有更好的方法?

这可能类似于Stephen Cleary在这里发表的评论,但他没有提供替代方案使用Task.Delay()的成本是多少?

  1. 万一ReadStatus无法返回,我正在尝试使用取消令牌来取消坐在那里等待响应的线程……这似乎不起作用。

    等待Task.Delay(50,tk)
    Thread.Sleep(100000)//模拟设备无响应

我仍然有大约20个工作线程处于活动状态(即使我期望cts.Cancel()杀死它们)。

foreach循环同时触发数百个任务

由于ReadStatus是同步的(我假设您不能更改此设置),并且由于每个人都必须独立,因为他们可以阻塞调用线程,所以您必须有数百个任务。 那已经是最有效的方法。

有没有更好的方法?

如果每个设备应该读每5秒钟,然后有自己独立的定时器每个设备可能会更好。 几个周期后,它们应“均匀”。

等待Task.Delay(50,tk);

我不建议使用Task.Delay来“蹦床”非异步代码。 如果您希望在线程池上运行代码,只需将其包装在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);
}

我正在尝试使用取消令牌来取消坐在那里等待响应的线程……这似乎不起作用。

取消令牌不会杀死线程。 如果ReadStatus观察到其取消令牌,则应取消; 如果没有,那么您将无能为力。

线程池中的线程不应该被终止; 这样可以减少下次计时器触发时的线程搅动。

如您在此取消标记的Microsoft示例页中所看到的, doWork方法正在检查每个循环上的取消。 因此,循环必须重新开始才能取消。 就您而言,当您模拟一个长任务时,它永远不会在运行时检查取消。

如何取消不可取消的异步操作? ,最后说:“那么,您可以取消不可取消的操作吗?不可以。您可以取消对不可取消的操作的等待吗?当然……在操作时要非常小心。” 因此它回答了我们无法取消它。

我建议的是将线程与ThreadPool一起使用,您需要每个线程的开始时间,并且您有一个较高优先级的线程,该线程可以查看其他线程是否绕过了它们的最大允许时间。 如果是这样,请使用Thread.Interrupt()

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM