如何正确使用实时优先级
我的问题可能并不是真正的实时处理,但话说可能是。
我的应用程序有几个比GUI重要得多的线程,但我确实希望GUI至少可以使用。 我不希望它一直被锁定,我确实想要更新屏幕给出我正在执行的处理结果。
目前我所有必需的项目都是在单独的线程中隔离的,我调用GUI的代理来显示结果。
我的GUI工作,但如果我更改标签,或最小化/最大化它,已知会阻碍我的其他线程,他们无法在0.1s的时间限制内执行操作。
这就是我打电话给我的代表所做的事情:
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事件吗?
如果没有我所有令人沮丧的问题的答案,是否有某种线程分析器可以用来帮助解决我的问题? 我尝试使用“托管堆栈资源管理器”,但不知何故,它并不总是显示我的应用程序有什么线程。
对此事的任何帮助都会对我有所帮助。
那么这是一个开始:
Invoke(DelegatedDisplay, Result);
这意味着您导致后台线程等待UI线程实际执行绘制操作,然后继续。 从线程的角度来看,这是永恒的。 您可能希望调查UI的异步更新:
BeginInvoke(DelegatedDisplay, Result);
这相当于告诉UI线程“当你有机会时,执行这个绘图操作”,然后继续你正在做的工作。
您应该知道,这可能会导致使用Invoke
不会发生的线程安全问题。 例如,如果后台线程在UI尝试绘制时仍在修改Result
,则可能会出现意外的竞争条件。
请参阅Control.Invoke与Control.BeginInvoke
使用诸如Invoke
和BeginInvoke
类的编组技术来更新UI是问题的一部分。 实际上,我很少使用编组操作来进行UI和工作线程交互,因为它不是一个很好的解决方案。 嗯,坦率地说,在大多数情况下,它可能(并且通常是)最糟糕的解决方案。
我通常做的是让工作线程将其结果或进度发布到共享数据结构,并使用System.Windows.Forms.Timer
(或DispatcherTimer
)对其进行UI线程轮询,其间隔被调整为最适合于手头的情况。
这可能是它的样子。
public class YourForm : Form { private ConcurrentQueue results = new ConcurrentQueue (); 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); } } }
问题是,人们已经被编程为自动使用Invoke
或BeginInvoke
更新UI,忽略更好的解决方案。 它已经达到了这些编组技术适合货物崇拜编程领域的程度。 我可能听起来像关于这个主题的破纪录,因为我一直在扯下它。 我上面使用的技术具有以下优点。
- 它打破了封送操作所强加的UI和工作线程之间的紧密耦合。
- 工作线程不必等待UI线程的响应,就像
Invoke
的情况一样。 - 与
BeginInvoke
的情况一样,没有机会使UI消息队列饱和。 - 您可以在UI和工作线程上获得更多吞吐量。
- UI线程决定了UI线程何时以及多久更新一次。
- 您不必使用
Invoke
或BeginInvoke
调用来丢弃您的代码(我的意思是字面意义)。 - 编组操作很昂贵。
- 代码最终看起来更优雅。
每次我需要并发进程时启动一个新线程会花费我宝贵的时间吗? 这应该是一个循环吗? 我的循环应该受到责备吗?
我会避免无缘无故地创建线程。 如果你可以保持线程在一个更好的循环中运行。
我可以给一个线程比其他线程更高的优先级,还是使用Thread.Sleep我唯一的选择? 如果我确定分配更高的线程优先级,我怎么能确定其他线程甚至可以存活?
在这种情况下,为您的工作线程赋予更高的优先级可能会有所帮助。 Thread.Sleep(5)
虽然不会睡5ms。 它只是不起作用。 顺便说一下,有一些特殊值可以传递给Thread.Sleep
。
- Thread.Sleep(0)产生任何处理器上具有相同或更高优先级的任何线程。
- Thread.Sleep(1)产生任何处理器上的任何线程。
为什么简单的表单事件会妨碍我的其他线程呢? 有没有办法为我的GUI线程分配较少量的资源? 如果其他线程的时钟时间不足,我可以使用Thread.Sleep以某种方式阻止Form事件吗?
这是因为你正在使用Invoke
。 避免编组操作将有很大帮助,因为它解耦了线程。 不要在UI线程上使用Thread.Sleep
。 UI线程必须保持解除阻塞状态才能正常工作。 如果您使用我上面提出的解决方案,那么限制UI线程就容易得多。