调用不切换回单独的线程
Posted
技术标签:
【中文标题】调用不切换回单独的线程【英文标题】:Invoke not switching back to separate thread 【发布时间】:2021-07-23 01:27:04 【问题描述】:我有下面的方法在与主 UI 线程不同的线程上运行,我正在尝试更新主线程上的 ListBox
控件。代码确实有效并且字段确实得到了更新,但是当Invoke
方法运行时,它会切换到主线程。问题是Invoke
之后的代码也在主线程上运行,但我需要它在单独的线程上运行。
public static void Status_Message(string str, int destination, int prompt)
//Clear_System_Message_Area();
sysmsg++;
ListBox tl = Application.OpenForms["GMLEC"].Controls["groupBox2"].Controls["TestList"] as ListBox;
if (!tl.InvokeRequired)
tl.Items.Add(str);
tl.Refresh();
else
tl.Invoke(new Action<string, int, int>(Status_Message), str, destination, prompt);
if (destination == 1)
Printer.Output(str);
if (prompt == 1)
Pause(false);
if (sysmsg > 23)
Pause(true);
有没有办法让它回到单独的线程?
【问题讨论】:
简单,不要调用整个函数,只调用你需要调用的。我也觉得每个同步调用的人都应该在他们的同行陪审团面前捍卫他们的选择,他们必须一致批准,或者他们需要修复他们的代码。 你怎么知道tl.Invoke
之前的代码在后台线程上运行,而在这行之后的代码在主UI线程上运行?我没有看到 Thread.CurrentThread.ManagedThreadId
被记录在某处。
【参考方案1】:
如果您不希望代码在 UI 线程上运行,请不要调用包含它的方法。
对于它的价值,我不同意任何使用InvokeRequired
的代码。首先,您应该从上下文中知道是否需要调用。如果您不知道正在执行的代码在哪个线程上,则说明 UI 和代码的后台任务部分之间存在过多的耦合。
但其次,Control.Invoke()
方法必须检查哪个线程是当前的,因为无论您是否在 UI 线程上,它都必须工作。您始终可以从 UI 线程安全地调用它,并且当您这样做时,它不能将您的委托排队等待调用,然后等待它,因为那样会死锁。它必须直接调用委托,但仅在这种情况下,这意味着它无论如何都要进行InvokeRequired
检查。
因此,考虑到所有这些因素,只需编写代码以始终调用需要调用的部分并完成它。
例如:
public static void Status_Message(string str, int destination, int prompt)
//Clear_System_Message_Area();
sysmsg++;
ListBox tl = Application.OpenForms["GMLEC"].Controls["groupBox2"].Controls["TestList"] as ListBox;
tl.Invoke((MethodInvoker)(() =>
tl.Items.Add(str);
tl.Refresh();
));
if (destination == 1)
Printer.Output(str);
if (prompt == 1)
Pause(false);
if (sysmsg > 23)
Pause(true);
现在,关于此的一些其他说明:
您是否应该致电Refresh()
令人怀疑。让 Winforms 自行处理更新。如果您以某种方式干扰了它正常刷新窗口,请修复那个。不要自己打电话给Refresh()
来绕过它。
几乎可以肯定,封装ListBox
对象比总是从UI 控制图的顶部向上查找更好的方法。例如,可能应该直接引用实际对象(例如,从 TestList
字段)并传递给最终需要它的代码。
最后,也是最重要的一点,您使用Invoke()
是现代代码中的一个重要警告标志。很有可能您的整体代码可以重构为使用 async/await 以使其更自然地读取并且仍然可以正常工作,但至少最好使用Progress<T>
来调解跨线程像这样的更新。
解决这些问题中的任何一个都超出了当前问题的范围,但我鼓励您考虑这些建议。
【讨论】:
【参考方案2】:这可能会有所帮助...
通常我使用Invoke()
在一段时间后激活部分脚本。 Invoke()
不重复,如果你想重复你可以使用InvokeRepeating()
。
另一种选择是使用“多线程”。以下是如何使用多线程:
using System.Threading
public static Thread newThread = new Thread(MultiThread)
private void Start()
newThread.Start()
//also newThread.Abort() to quit the thread
private static void MultiThread()
// this is the seporate thread
// i normally use this for a "while (True)" loop cause it will stop responding
//otherwise
如有错别字请见谅
希望这会有所帮助
【讨论】:
以上是关于调用不切换回单独的线程的主要内容,如果未能解决你的问题,请参考以下文章
29.Windows线程切换之被动切换(KiDispatchInterrupt)