简体   繁体   English

高性能异步监控任务

[英]High performance async monitoring tasks

I have a couple of hundred devices and I need to check their status every 5 seconds. 我有数百个设备,我需要每5秒检查一次它们的状态。

The API I'm using contains a blocking function that calls a dll and returns a status of a single device 我正在使用的API包含一个阻止函数,该函数调用dll并返回单个设备的状态

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

The above function usually returns the status in a couple of ms, but there will be situations where I might not get the status back for a second or more! 上面的函数通常在几毫秒内返回状态,但是在某些情况下,我可能一秒钟或更长时间都无法恢复状态! Or even worse, one device might not respond at all. 甚至更糟的是,一台设备可能根本不响应。

I therefore need to introduce a form of asynchronicity to make sure that one device that doesn't respond doesn't impend all the others being monitored. 因此,我需要引入一种异步形式,以确保一个无响应的设备不会阻止其他所有受监视的设备。

My current approach is as following 我目前的方法如下

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

And below in the Device class 并在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);
}

I'm having several problems with my code 我的代码有几个问题

  1. the foreach loops fires a couple of hundred tasks simultaneously , with the callback from the Task.Delay being served by a thread from the thread pool, each task taking a couple of ms. foreach循环同时触发数百个任务 ,其中Task.Callback的回调由线程池中的线程提供服务,每个任务花费几毫秒。

I see this as a big potential bottleneck. 我认为这是一个巨大的潜在瓶颈。 Are there any better approaches? 有没有更好的方法?

This might be similar to what Stephen Cleary commented here, but he didn't provide an alternative What it costs to use Task.Delay()? 这可能类似于Stephen Cleary在这里发表的评论,但他没有提供替代方案使用Task.Delay()的成本是多少?

  1. In case ReadStatus fails to return, I'm trying to use a cancellation token to cancel the thread that sits there waiting for the response... This doesn't seem to work. 万一ReadStatus无法返回,我正在尝试使用取消令牌来取消坐在那里等待响应的线程……这似乎不起作用。

    await Task.Delay(50, tk) 等待Task.Delay(50,tk)
    Thread.Sleep(100000) // simulate the device not responding Thread.Sleep(100000)//模拟设备无响应

I still have about 20 Worker Threads alive (even though I was expecting cts.Cancel() to kill them. 我仍然有大约20个工作线程处于活动状态(即使我期望cts.Cancel()杀死它们)。

the foreach loops fires a couple of hundred tasks simultaneously foreach循环同时触发数百个任务

Since ReadStatus is synchronous (I'm assuming you can't change this), and since each one needs to be independent because they can block the calling thread, then you have to have hundreds of tasks. 由于ReadStatus是同步的(我假设您不能更改此设置),并且由于每个人都必须独立,因为他们可以阻塞调用线程,所以您必须有数百个任务。 That's already the most efficient way. 那已经是最有效的方法。

Are there any better approaches? 有没有更好的方法?

If each device should be read every 5 seconds, then each device having its own timer would probably be better. 如果每个设备应该读每5秒钟,然后有自己独立的定时器每个设备可能会更好。 After a few cycles, they should "even out". 几个周期后,它们应“均匀”。

await Task.Delay(50, tk); 等待Task.Delay(50,tk);

I do not recommend using Task.Delay to "trampoline" non-async code. 我不建议使用Task.Delay来“蹦床”非异步代码。 If you wish to run code on the thread pool, just wrap it in a Task.Run : 如果您希望在线程池上运行代码,只需将其包装在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);
}

I'm trying to use a cancellation token to cancel the thread that sit there waiting for the response... This doesn't seem to work. 我正在尝试使用取消令牌来取消坐在那里等待响应的线程……这似乎不起作用。

Cancellation tokens do not kill threads. 取消令牌不会杀死线程。 If ReadStatus observes its cancellation token, then it should cancel; 如果ReadStatus观察到其取消令牌,则应取消; if not, then there isn't much you can do about it. 如果没有,那么您将无能为力。

Thread pool threads should not be terminated; 线程池中的线程不应该被终止; this reduces thread churn when the timer next fires. 这样可以减少下次计时器触发时的线程搅动。

As you can see in this Microsoft example page of a cancellation token , the doWork method is checking for cancellation on each loop. 如您在此取消标记的Microsoft示例页中所看到的, doWork方法正在检查每个循环上的取消。 So, the loop has to start again to cancel out. 因此,循环必须重新开始才能取消。 In your case, when you simulate a long task, it never checks for cancellation at all when it's running. 就您而言,当您模拟一个长任务时,它永远不会在运行时检查取消。

From How do I cancel non-cancelable async operations? 如何取消不可取消的异步操作? , it's saying at the end : "So, can you cancel non-cancelable operations? No. Can you cancel waits on non-cancelable operations? Sure… just be very careful when you do.". ,最后说:“那么,您可以取消不可取消的操作吗?不可以。您可以取消对不可取消的操作的等待吗?当然……在操作时要非常小心。” So it answers that we can't cancel it out. 因此它回答了我们无法取消它。

What I would suggest is to use threads with a ThreadPool, you take the starting time of each one and you have an higher priority thread that looks if others bypass their maximum allowed time. 我建议的是将线程与ThreadPool一起使用,您需要每个线程的开始时间,并且您有一个较高优先级的线程,该线程可以查看其他线程是否绕过了它们的最大允许时间。 If so, Thread.Interrupt() . 如果是这样,请使用Thread.Interrupt()

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

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