这个问题是关于如何在UI线程上重新执行-InvokeRequired
返回false,而我认为它应该返回true。我有一个简单的Windows窗体应用程序用于测试和调试。将行消息写入VStudio中的输出窗口以跟踪线程和操作。
我的目标(或应用架构)是让所有的动作初始化(菜单或其他调用者/发起者)都通过一个接口层,该接口层为调用线程提供异步/等待服务,以便在接口工作线程和方法完成工作时释放它们。
问题是InvokeRequired测试返回false。当我跳过测试,只调用Invoke((Action) FormCloseDutiesHelper)
时,错误消息说控件的句柄(我想是Form 1)还没有创建。但是当然,Form 1已经创建了-我正在单击File/Close菜单项!
我读了很多关于InvokeRequired和优化Code Pattern的SO文章,但是我没有发现任何东西可以解释为什么InvokeRequired认为“the”控件没有句柄。鼠标放在“this.InvokeRequired”上说“this”是MyDemoProgram.Form1对象。我不知道怎么解释这是怎么回事。
下面是我使用的测试代码,从菜单中单击“代码”。
Program.cs:
internal static class Program
{
public static readonly Form1 PublicForm1 = new();
Form1.cs:
public partial class Form1 : Form
{
public Form1() {
InitializeComponent();
}
async void closeToolStripMenuItem_Click(object sender, EventArgs e) {
var me = "CloseMenuItemClick";
var msg = $"{me} Thread id {Thread.CurrentThread.ManagedThreadId}";
Debug.WriteLine(msg);
// this frees the UI thread up for other work - and it does, as shown by the trace below
await TaskHelperClose();
}
Interface.cs:
public static async Task<bool> TaskHelperClose() {
var me = "TaskHelperClose";
var msg = $"{me} Thread id {Thread.CurrentThread.ManagedThreadId}";
Debug.WriteLine(msg);
// this lets the calling thread go away immediately
await Task.Run(HelperClose);
// just so that the Task does not return void
return true;
}
Interface.cs:
// this runs on the new worker thread
internal static void HelperClose() {
var me = MethodBase.GetCurrentMethod()?.Name;
var msg = $"{me} Thread id {Thread.CurrentThread.ManagedThreadId}";
Debug.WriteLine(msg);
// close means put the window in the tray
// FormCloseCaller tests InvokeRequired to get the work done on the UI thread
PublicForm1.FormCloseCaller();
}
Form1.cs:
// this is still running on the worker thread
internal void FormCloseCaller() {
var me = MethodBase.GetCurrentMethod()?.Name;
var msg = $"{me} Thread id {Thread.CurrentThread.ManagedThreadId}";
Debug.WriteLine(msg);
// this is supposed to return true for the menu I just clicked on, but it does not.
if (this.InvokeRequired) {
Debug.Writeline($"{me} Invoke required.");
Invoke((Action) FormCloseDutiesHelper);
}
else {
Debug.Writeline($"{me} Invoke NOT required.");
FormCloseDutiesHelper();
}
}
Form1.cs:
void FormCloseDutiesHelper() {
var me = MethodBase.GetCurrentMethod()?.Name;
var msg = $"{me} Thread id {Thread.CurrentThread.ManagedThreadId}";
Debug.WriteLine(msg);
msg = $"{me} Thread id {Thread.CurrentThread.ManagedThreadId}"
+ " Setting window state to minimized";
Debug.WriteLine(msg);
WindowState = FormWindowState.Minimized;
ShowInTaskbar = false;
notifyIcon1.Text = @"Click or double click to open";
notifyIcon1.Visible = true;
}
下面是执行中线程的轨迹。UI线程是1,await/async/worker线程是9,Invoke不是必需的(即使worker线程不是UI线程),表单没有最小化。
CloseMenuItemClick Thread id 1 - the UI thread
TaskHelperClose Thread id 1
HelperClose Thread id 9 - the await Task.Run(HelperClose) thread
FormCloseCaller Thread id 9 - this guy tests InvokeRequired
FormCloseCaller Invoke NOT required. - and concludes that Invoke is NOT required
FormCloseDutiesHelper Thread id 9 - and used the worker thread to call the helper code
FormCloseDutiesHelper Thread id 9 Setting window state to minimized
- and the window was not minimized.
我也没有得到跨线程异常,除非我跳过InvokeRequired测试并直接调用FormCloseDutiesHelper方法。
有谁能解释一下这是怎么回事,以及我如何在原始UI线程上完成工作?谢谢。
1条答案
按热度按时间gwbalxhn1#
正如@KlausGutter所建议的,我的程序中确实有两个表单。在他发布答案的同时,我发现了这个问题。下面是违规代码。我使用PublicForm 1使表单对其他类可见,当我定义它时,我忘记将名称放入下面显示的
Application.Run(new Form1());
语句中。一旦我将PublicForm 1放入Run语句中,一切都开始按预期工作。