通常、非同期デッドロックはUIスレッドまたはASP.NETコンテキストで発生します。私は私のライブラリコードを単体テストできるようにコンソールアプリケーションでデッドロックをシミュレートしようとしています。コンソールアプリケーションでの非同期デッドロックのシミュレーション
だからここに私の試みです:
class Program
{
private static async Task DelayAsync()
{
Console.WriteLine("DelayAsync.Start");
await Task.Delay(1000);
Console.WriteLine("DelayAsync.End");
}
// This method causes a deadlock when called in a GUI or ASP.NET context.
public static void Deadlock()
{
Console.WriteLine("Deadlock.Start");
// Start the delay.
var delayTask = DelayAsync();
// Wait for the delay to complete.
delayTask.Wait();
Console.WriteLine("Deadlock.End");
}
static void Main(string[] args)
{
var thread = new Thread(() =>
{
Console.WriteLine("Thread.Start");
SynchronizationContext.SetSynchronizationContext(new DedicatedThreadSynchronisationContext());
Deadlock();
Console.WriteLine("Thread.End");
});
thread.Start();
Console.WriteLine("Thread.Join.Start");
thread.Join();
Console.WriteLine("Thread.Join.End");
Console.WriteLine("Press any key to exit");
Console.ReadKey(true);
Console.WriteLine("Pressed");
}
}
のでデッドロック()は右文脈でデッドロックが発生する必要があります。
SynchronizationContext.SetSynchronizationContext(new DedicatedThreadSynchronisationContext());
が、私はコードがハングアップすることを期待:私はデッドロック()を呼び出す前にコンテキストを設定
public sealed class DedicatedThreadSynchronisationContext : SynchronizationContext, IDisposable
{
public DedicatedThreadSynchronisationContext()
{
m_thread = new Thread(ThreadWorkerDelegate);
m_thread.Start(this);
}
public void Dispose()
{
m_queue.CompleteAdding();
}
/// <summary>Dispatches an asynchronous message to the synchronization context.</summary>
/// <param name="d">The System.Threading.SendOrPostCallback delegate to call.</param>
/// <param name="state">The object passed to the delegate.</param>
public override void Post(SendOrPostCallback d, object state)
{
if (d == null) throw new ArgumentNullException("d");
m_queue.Add(new KeyValuePair<SendOrPostCallback, object>(d, state));
}
/// <summary> As
public override void Send(SendOrPostCallback d, object state)
{
using (var handledEvent = new ManualResetEvent(false))
{
Post(SendOrPostCallback_BlockingWrapper, Tuple.Create(d, state, handledEvent));
handledEvent.WaitOne();
}
}
public int WorkerThreadId { get { return m_thread.ManagedThreadId; } }
//=========================================================================================
private static void SendOrPostCallback_BlockingWrapper(object state)
{
var innerCallback = (state as Tuple<SendOrPostCallback, object, ManualResetEvent>);
try
{
innerCallback.Item1(innerCallback.Item2);
}
finally
{
innerCallback.Item3.Set();
}
}
/// <summary>The queue of work items.</summary>
private readonly BlockingCollection<KeyValuePair<SendOrPostCallback, object>> m_queue =
new BlockingCollection<KeyValuePair<SendOrPostCallback, object>>();
private readonly Thread m_thread = null;
/// <summary>Runs an loop to process all queued work items.</summary>
private void ThreadWorkerDelegate(object obj)
{
SynchronizationContext.SetSynchronizationContext(obj as SynchronizationContext);
try
{
foreach (var workItem in m_queue.GetConsumingEnumerable())
workItem.Key(workItem.Value);
}
catch (ObjectDisposedException) { }
}
}
: は、私はDedicatedThreadSynchronisationContext https://stackoverflow.com/a/31714115/121240からを使用しています、ASP.NETのコンテキストをシミュレートするために、この行はコンテキストをキャプチャする必要があるためです。
await Task.Delay(1000);
ただし、 dプログラムは最後まで実行され、 "Pressed"が印刷されます。 (プログラムはDedicatedThreadSynchronisationContext.ThreadWorkerDelegate()にハングアップしますが、存在しませんが、小さな問題だと思います。)
デッドロックが発生しないのはなぜですか?デッドロックをシミュレートする適切な方法は何ですか?
========================================
EDIT
Luaanによって答えを1として、
私は(DedicatedThreadSynchronisationContext.Sendを使用)の代わりに、新しいスレッドを作成する:
Console.WriteLine("Send.Start");
var staContext = new DedicatedThreadSynchronisationContext();
staContext.Send((state) =>
{
Deadlock();
}, null);
Console.WriteLine("Send.End");
それは(デッドロックをすることができます)コンテキストで実行するので、「待ちます」同じコンテキストを捕捉し、デッドロックが発生します。
ありがとうございました!
を与えることは理にかなっています。 DedicatedThreadSynchronisationContextでsend()を呼び出し、デッドロックであるvoilaを呼び出しました。 – wooohoh