如何正确使用实时优先级
本文关键字:实时 优先级 何正确 | 更新日期: 2023-09-27 18:36:05
我的问题可能不是关于实时处理的,但话又说回来,它可能是。
我的应用程序有几个线程,它们比 GUI 重要得多,但是,我确实希望 GUI 至少是可用的。我不希望它始终被锁定,并且我确实想根据我正在执行的处理结果更新屏幕。
目前,我的所有基本项目都隔离在单独的线程中,我调用 GUI 的委托以显示结果。
我的GUI 可以工作,但是如果我更改选项卡或最小化/最大化它,众所周知,它会阻碍我的其他线程,以至于它们无法在 0.1 秒的时间限制内执行操作。
这就是我打电话给我的代表所做的:
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 来阻止表单事件?
除了我所有令人沮丧的问题的答案之外,我可以使用某种线程分析器来帮助解决我的混乱吗?我尝试使用"托管堆栈资源管理器",但不知何故,这并不总是显示我的应用程序具有哪些线程。
在这件事上的任何帮助都会对我有很大帮助。
好吧,这是一个开始:
Invoke(DelegatedDisplay, Result);
这意味着您会导致后台线程等待 UI 线程实际执行绘制操作,然后继续。 从线程的角度来看,这是一个永恒。 您可能需要调查 UI 的异步更新:
BeginInvoke(DelegatedDisplay, Result);
这相当于告诉 UI 线程"当你有机会时,执行此绘制操作",然后继续你正在做的工作。
您应该知道,这可能会导致使用 Invoke
未发生的线程安全问题。 例如,如果在 UI 尝试绘制时后台线程仍在修改Result
则可能会出现意外的争用条件。
请参阅 Control.Invoke vs Control.BeginInvoke
使用封送处理技术(如 Invoke
和 BeginInvoke
)来更新 UI 是问题的一部分。事实上,我很少对 UI 和工作线程交互使用封送处理操作,因为它不是那么好的解决方案。好吧,坦率地说,在大多数这种性质的情况下,它可能是(并且通常是)最糟糕的解决方案。
我通常做的是让工作线程将其结果或进度发布到共享数据结构,并让 UI 线程使用System.Windows.Forms.Timer
(或DispatcherTimer
)轮询它,时间间隔经过调整以最适合手头的情况。
这是它可能的样子。
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);
}
}
}
问题是,人们已经被编程为自动使用Invoke
或BeginInvoke
来更新UI,以至于他们忽略了更好的解决方案。它已经到了这些编组技术适合货物崇拜编程领域的地步。关于这个话题,我可能听起来像是一张破纪录,因为我一直在撕毁它。我上面使用的技术具有以下优点。
- 它打破了封送处理操作强加的 UI 和工作线程之间的紧密耦合。
- 工作线程不必像
Invoke
那样等待来自 UI 线程的响应。 - 没有机会像
BeginInvoke
那样使 UI 消息队列饱和。 - 您可以在 UI 和工作线程上获得更多吞吐量。 UI 线程
- 可以指示何时以及多久更新一次 UI 线程。
- 您不必用
Invoke
或BeginInvoke
调用乱扔代码(我的意思是字面意思)。 - 编组操作成本高昂。
- 代码最终看起来更优雅。
每次我需要并发进程时启动一个新线程 耗费了我宝贵的时间?这应该是一个循环吗?我的循环是罪魁祸首吗?
我会避免随意创建线程。如果可以让线程保持循环运行,那就更好了。
我可以给一个线程比其他线程更高的优先级吗,还是使用 Thread.睡觉是我唯一的选择?如果我确实分配了更高的线程优先级, 我怎么能确定其他线程甚至可以存活?
在这种情况下,为工作线程提供更高的优先级可能会有所帮助。 不过,Thread.Sleep(5)
不会睡 5 毫秒。它只是不是那样工作的。顺便说一下,您可以传递给一些特殊值 Thread.Sleep
.
- Thread.Sleep(0) 产生任何处理器上具有相同或更高优先级的任何线程。
- Thread.Sleep(1) 产生于任何处理器上的任何线程。
为什么简单表单事件如此阻碍我的其他线程?是 有一种方法可以为我的 GUI 线程分配一个较少数量的 资源?我可以使用 Thread.Sleep 以某种方式阻止表单事件吗? 其他线程的时钟时间用完了?
这是因为您正在使用 Invoke
.避免封送操作将有很大帮助,因为它会分离线程。不要在 UI 线程上使用Thread.Sleep
。UI 线程必须保持畅通无阻,才能正常工作。如果您使用我上面建议的解决方案,那么限制 UI 线程要容易得多。