在更新我的 UI 代码(.NET 4.0 应用程序中的 C#)时,由于在错误线程中执行对 UI 的调用,我遇到了奇怪的崩溃。但是,我已经在主线程上调用了该调用,因此崩溃没有任何意义:MainThreadDispatcher.Invoke(new Action(View.Method))
因“调用线程无法访问此对象,因为不同的线程拥有它”而崩溃。在 View 属性上。
经过进一步调查,我找到了原因:我正在通过方法组调用。我曾认为使用方法组或委托/lambda 本质上是相同的(另请参见这个问题和这个问题)。相反,将方法组转换为委托会导致代码执行,检查View
. 这是立即完成的,即在导致崩溃的原始(非 UI)线程上。如果我改用 lambda,则稍后会在正确的线程中检查属性。
至少可以这么说,这似乎很有趣。C# 标准中是否有提到这一点的地方?还是由于需要找到正确的转换而隐含?
这是一个测试程序。一是直接方式。其次,分两个步骤,更好地显示发生了什么。为了获得更多乐趣,我会Item
在创建委托后进行修改。
namespace ConsoleApplication1 // Add a reference to WindowsBase to a standard ConsoleApplication
{
using System.Threading;
using System.Windows.Threading;
using System;
static class Program
{
static Dispatcher mainDispatcher;
static void Main()
{
mainDispatcher = Dispatcher.CurrentDispatcher;
mainDispatcher.Thread.Name = "Main thread";
var childThread = new Thread(() =>
{
Console.WriteLine("--- Method group ---");
mainDispatcher.Invoke(new Action(Item.DoSomething));
Console.WriteLine("\n--- Lambda ---");
mainDispatcher.Invoke(new Action(() => Item.DoSomething()));
Console.WriteLine("\n--- Method group (two steps) ---");
var action = new Action(Item.DoSomething);
Console.WriteLine("Invoking");
mainDispatcher.Invoke(action);
Console.WriteLine("\n--- Lambda (two steps) ---");
action = new Action(() => Item.DoSomething());
Console.WriteLine("Invoking");
mainDispatcher.Invoke(action);
Console.WriteLine("\n--- Method group (modifying Item) ---");
action = new Action(Item.DoSomething);
item = null;
mainDispatcher.Invoke(action);
item = new UIItem();
Console.WriteLine("\n--- Lambda (modifying Item) ---");
action = new Action(() => Item.DoSomething());
item = null;
Console.WriteLine("Invoking");
mainDispatcher.Invoke(action);
mainDispatcher.InvokeShutdown();
});
childThread.Name = "Child thread";
childThread.Start();
Dispatcher.Run();
}
static UIItem item = new UIItem();
static UIItem Item
{
get
{
// mainDispatcher.VerifyAccess(); // Uncomment for crash.
Console.WriteLine("UIItem: In thread: {0}", Dispatcher.CurrentDispatcher.Thread.Name);
return item;
}
}
private class UIItem
{
public void DoSomething()
{
Console.WriteLine("DoSomething: In thread: {0}", Dispatcher.CurrentDispatcher.Thread.Name);
}
}
}
}
简洁版本:
namespace ConsoleApplication1 // Add a reference to WindowsBase to a standard ConsoleApplication
{
using System.Threading;
using System.Windows.Threading;
using System;
static class Program
{
static Dispatcher mainDispatcher;
static void Main()
{
mainDispatcher = Dispatcher.CurrentDispatcher;
mainDispatcher.Thread.Name = "Main thread";
var childThread = new Thread(() =>
{
Console.WriteLine("--- Method group ---");
mainDispatcher.Invoke(new Action(Item.DoSomething));
Console.WriteLine("\n--- Lambda ---");
mainDispatcher.Invoke(new Action(() => Item.DoSomething()));
mainDispatcher.InvokeShutdown();
});
childThread.Name = "Child thread";
childThread.Start();
Dispatcher.Run();
}
static UIItem item = new UIItem();
static UIItem Item
{
get
{
mainDispatcher.VerifyAccess();
Console.WriteLine("UIItem: In thread: {0}", Dispatcher.CurrentDispatcher.Thread.Name);
return item;
}
}
private class UIItem
{
public void DoSomething()
{
Console.WriteLine("DoSomething: In thread: {0}", Dispatcher.CurrentDispatcher.Thread.Name);
}
}
}
}