这个问题是关于如何在UI线程上执行-当我认为它应该返回true时,InvokeRequired
返回false。我有一个简单的Windows窗体应用程序的测试和调试。向VStudio的输出窗口写入消息,以跟踪线程和操作。
我的目标(或应用架构)是让所有的动作启动(菜单或其他调用者/启动者)通过一个接口层,该接口层为调用线程提供async/await服务,这样当接口工作线程和方法完成工作时,它们可以被释放。
问题是invokerrequiredtest返回false。当我跳过测试并只是调用Invoke((Action) FormCloseDutiesHelper)
时,错误消息说控件的句柄(我认为Form1)尚未创建。但是,当然,Form1已经创建-我点击文件/关闭菜单项!
我已经读了一打关于invokerrequired和优化代码模式的方法的SO帖子,但我没有发现任何解释为什么invokerrequired认为"控件没有句柄。鼠标悬停在"this. invokerrequired"表示"这个"是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,不需要调用(即使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.
我也没有得到跨线程异常,除非我跳过invokerrequiredtest并直接调用FormCloseDutiesHelper方法。
有谁能解释一下发生了什么,以及我如何在原始UI线程上完成工作?谢谢你。
正如@KlausGutter所建议的,我的程序中实际上有两个表单。我正好在他发表答案的同时发现了这个问题。下面是违规代码。我使用PublicForm1使表单对其他类可见,当我定义它时,我忘记将名称放在Application.Run(new Form1());
语句中,如下所示。一旦我将PublicForm1放入Run语句中,一切都开始按预期工作。
internal static class Program
{
public static readonly Form1 PublicForm1 = new();
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
// To customize application configuration such as set high DPI settings or default font,
// see https://aka.ms/applicationconfiguration.
ApplicationConfiguration.Initialize();
Application.Run(new Form1());