如何正确使用实时优先级

时间:2012-05-31 13:11:04

标签: c# winforms multithreading real-time

我的问题可能并不是真正的实时处理,但话说可能是。

我的应用程序有几个线程比GUI重要得多,但我确实希望GUI至少可以使用。我不希望它一直处于锁定状态,我确实想要根据我正在执行的处理结果更新屏幕。

目前我所有必需的项目都是在单独的线程中隔离的,我调用GUI的代理来显示结果。

我的GUI工作正常,但如果我更改标签,或最小化/最大化它,已知会阻碍我的其他线程,使其无法在0.1秒的时间限制内执行操作。< / p>

这就是我打电话给我的代表所做的事情:

delegate void FuncDelegate(ResultContainer Result);
FuncDelegate DelegatedDisplay= new FuncDelegate(DisplayResults);
//then later on
Invoke(DelegatedDisplay, Result);

我的大部分关键进程都是在连续循环中运行的线程,从各种缓冲区(ArrayLists和普通列表)中拉出并推送。

每次启动我的一个关键线程,使用:

Thread mythread = new Thread(new ThreadStart(ProcessResults));
mythread.Start();

我之所以这么做,而不是仅仅让一个线程在一个循环中运行,从列表中拉出来,是因为我认为我的时钟时间不足的原因是我有一个我担心的轮询循环消耗太多资源(虽然每次轮询结果为负时我都使用Thread.Sleep(5)。

每次需要并发进程时启动新线程会花费我宝贵的时间吗?这应该是一个循环吗?我的循环是否应该受到责备?

我可以给一个线程比其他线程更高的优先级,还是使用Thread.Sleep我唯一的选择?如果我确定了更高的线程优先级,我怎么能确定其他线程能够存活?

为什么简单的表单事件会妨碍我的其他线程呢?有没有办法为我的GUI线程分配更少量的资源?如果其他线程的时钟时间不足,我可以使用Thread.Sleep以某种方式阻止Form事件吗?

对我所有令人沮丧的问题的答案都没有,是否有某种线程分析器可以用来帮助解决我的问题?我尝试使用“托管堆栈资源管理器”但不知何故并不总是显示我的应用程序有什么线程。

对此事的任何帮助都会对我有所帮助。

2 个答案:

答案 0 :(得分:5)

这是一个开始:

Invoke(DelegatedDisplay, Result);

这意味着您要让后台线程等到UI线程实际执行绘图操作,然后继续。从线程的角度来看,这是永恒的。您可能希望调查UI的异步更新:

BeginInvoke(DelegatedDisplay, Result);

这相当于告诉UI线程“当你有机会时,执行此绘图操作”,然后继续你正在做的工作。

您应该知道,这可能会导致使用Invoke时未发生的线程安全问题。例如,如果后台线程在UI尝试绘制时仍在修改Result,则可能会出现意外的竞争条件。

请参阅Control.Invoke vs Control.BeginInvoke

答案 1 :(得分:1)

使用InvokeBeginInvoke之类的编组技术来更新UI是问题的一部分。事实上,我很少使用编组操作来进行UI和工作线程交互,因为它不是一个很好的解决方案。嗯,坦率地说,在大多数情况下,这可能是(并且通常是)最糟糕的解决方案。

我通常做的是让工作线程将其结果或进度发布到共享数据结构,并使用System.Windows.Forms.Timer(或DispatcherTimer)对其进行UI线程轮询。调整为最适合当前的情况。

这可能是它的样子。

public class YourForm : Form
{
  private ConcurrentQueue<ResultContainer> results = new ConcurrentQueue<ResultContainer>();

  public UpdateTimer_Tick(object sender, EventArgs args)
  {
    // Limit the number of results to be processed on each cycle so that
    // UI does not stall for too long.
    int maximumResultsToProcessInThisBatch = 100;

    ResultContainer result;
    for (int i = 0; i < maximumResultsToProcessInThisBatch; i++)
    {
      if (!results.TryDequeue(out result)) break;
      UpdateUiControlsHere(result);
    }
  }

  private void WorkerThread()
  {
    while (true)
    {
      // Do work here.
      var result = new ResultContainer();
      result.Item1 = /* whatever */;
      result.Item2 = /* whatever */;
      // Now publish the result.
      results.Enqueue(result);
    }
  }
}

问题是,人们已经过如此编程,可以自动使用InvokeBeginInvoke来更新UI,忽略更好的解决方案。它已经达到了这些编组技术适合cargo cult programming领域的程度。我可能听起来像是关于这个主题的破纪录,因为我一直在扯下它。我上面使用的技术具有以下优点。

  • 它打破了封送操作所强加的UI和工作线程之间的紧密耦合。
  • 工作线程不必等待来自UI线程的响应,就像Invoke一样。
  • BeginInvoke的情况一样,没有机会使UI消息队列饱和。
  • 您可以在UI和工作线程上获得更多吞吐量。
  • UI线程决定了UI线程的更新时间和频率。
  • 您不必在InvokeBeginInvoke来电时乱丢您的代码(我的意思是字面意思)。
  • 编组操作非常昂贵。
  • 代码最终看起来更优雅。
  

每次需要并发进程时都会启动新线程   花了我宝贵的时间?这应该是一个循环吗?我的循环是否应该受到责备?

我会避免无缘无故地创建线程。如果你可以保持线程在一个更好的循环中运行。

  

我可以给一个线程比其他线程更高的优先级,或者是使用   Thread.Sleep我唯一的选择?如果我确定分配更高的线程优先级,   我怎么能确定其他线程能够存活下来?

在这种情况下,为您的工作线程赋予更高的优先级可能会有所帮助。但Thread.Sleep(5)不会睡5ms。它只是不起作用。顺便说一下,您可以将一些特殊值传递给Thread.Sleep

  • Thread.Sleep(0)产生任何处理器上具有相同或更高优先级的任何线程。
  • Thread.Sleep(1)产生任何处理器上的任何线程。
  

为什么简单的表单事件会妨碍我的其他线程呢?是   有一种方法可以为我的GUI线程分配较低的数量   资源?我可以使用Thread.Sleep以某种方式阻止Form事件,如果   其他线程的时钟时间用完了吗?

这是因为您正在使用Invoke。避免编组操作将有很大帮助,因为它解耦了线程。不要在UI线程上使用Thread.Sleep。 UI线程必须保持解除阻止才能正常工作。如果您使用我上面提出的解决方案,那么限制UI线程就容易得多。