C#等待列表<t>计数

时间:2016-07-15 04:43:41

标签: c# .net multithreading winforms async-await

我正在升级一些遗留的 WinForms 代码,我试图找出 .NET 4.6.1 的“正确方法”来重构以下内容。< / p>

当前代码在检查while(true)属性时执行紧密bool循环。此属性将lock()放在通用List<T>上,如果没有项目(list.Count == 0)则返回true。

循环中有可怕的Application.DoEvents()以确保消息泵继续处理,否则会锁定应用程序。

显然,这需要进行。

我的困惑是如何启动一个基本的重构,它仍然可以检查队列长度,同时在线程上执行而不是无缘无故地吹灭CPU。这里检查之间的延迟很好,即使像100ms +这样的“长”也是如此。

我打算采用一种让方法异步的方法,让任务运行来检查:

await Task.Run(() => KeepCheckingTheQueue());

当然,这使我处于需要循环以检查队列状态的方法的情况。

在等待,等待和各种其他方法之间,可以用来将这些东西移动到线程池......关于如何最好地处理这个问题的任何建议?

4 个答案:

答案 0 :(得分:4)

  

我需要的是如何在释放UI时最好地“轮询”布尔成员(或属性),而不使用DoEvents()。

你要求的答案:

private async Task WaitUntilAsync(Func<bool> func)
{
  while (!func())
    await Task.Delay(100);
}

await WaitUntilAsync(() => list.Count == 0);

然而,像这样的民意调查是一种非常糟糕的方法。如果您可以描述代码正在解决的实际问题,那么您可以获得更好的解决方案。

例如,如果列表代表某个工作队列,并且您的代码想要异步等待直到完成,那么可以使用显式信号(例如TaskCompletionSource<T>)或真正的生成器更好地编码/消费者队列(例如,TPL数据流)。

答案 1 :(得分:1)

对于客户端代码而言,在查询之前担心锁定集合(或在任何地方用lock()块填充代码)通常都不是一个好主意。最好将这种复杂性封装起来。

相反,我建议使用其中一个.NET并发集合,例如ConcurrentBag。无需创建有点贵的Task

如果您的收藏品没有太大变化,您可能需要考虑一个不可变的线程安全的收藏集,例如ImmutableList<>

编辑:在阅读您的评论后,我建议您使用WinForms Timer; OnApplicationIdleBackgroundWorkerasync的问题是您仍需要定期调用它。使用计时器或应用程序空闲回调提供了使用GUI线程的好处。

答案 2 :(得分:0)

根据用例,您可以启动后台线程或后台工作程序。或者甚至可能是计时器。

这些是在不同的线程中执行的,因此不会锁定其他表单相关代码的执行。如果您必须在UI线程上执行操作,则Invoke原始线程。

我还建议尽可能地防止锁定,例如在实际锁定之前进行检查:

if (list.Count == 0)
{
    lock (lockObject)
    {
        if (list.Count == 0)
        {
            // execute your code here
        }
    }
}

这样,只有你确实需要锁定才能避免不必要的应用程序阻塞。

答案 3 :(得分:0)

我认为你在此之后的是能够await Task.Yield()

class TheThing {
  private readonly List<int> _myList = new List<int>();

  public async Task WaitForItToNotBeEmpty() {
    bool hadItems;
    do {
      await Task.Yield();
      lock (_myList) // Other answers have touched upon this locking concern
        hadItems = _myList.Count != 0;
    } while (!hadItems);
  }
  // ...
}