UIコード(.NET 4.0アプリケーションではC#)を更新している間に、間違ったスレッドでUIが呼び出されたために異常なクラッシュが発生しました。しかし、私はすでにメインスレッドでその呼び出しを呼び出していたので、クラッシュは意味をなさない。MainThreadDispatcher.Invoke(new Action(View.Method))
が "別のスレッドがそれを所有しているため、このオブジェクトにアクセスすることができない"とクラッシュした。 Viewプロパティで設定します。C#メソッドグループを使用してコードを実行します
さらに調査したところ、私は原因を見つけました:私はメソッドグループ経由で呼び出していました。メソッドグループやデリゲート/ラムダを使用することは本質的に同じことと考えていました(this questionとthis questionも参照)。代わりに、メソッドグループを代理人に変換すると、コードが実行され、View
の値がチェックされます。これは即座に行われます。つまり、元のUIではないのスレッドがクラッシュしました。代わりにラムダを使用すると、プロパティのチェックが後で行われ、正しいスレッドで行われます。
それは面白いと言えます。 これはC#標準のどこに記載されていますか?それとも、正しい変換を見つける必要があるために暗黙のことですか?
ここにテストプログラムがあります。まず、直接の方法。第二に、何が起こるかをより良く示す2つのステップである。さらに楽しみのために、代理人が作成された後に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);
}
}
}
}
どの呼び出しが失敗しますか?コールスタックとは何ですか? – SLaks
VerifyAccess()でコメント行を削除すると、Itemプロパティが子スレッドでアクセスされるため、メソッドグループを使用するすべての呼び出しが失敗することがわかります。 –
問題のあるコールを表示する、短くて完全なプログラム*ちょっとしたことが本当に役に立ちます。 –