我正在尝试创建一个"消息泵"类似于在线程上运行的自定义同步上下文。
该程序是Silverlight 5,同步队列来自Stephen Cleary的Nito.AsyncEx nuget(v3.0.1)。
代码(抱歉长度,故意包含评论/调试):
public sealed class ThreadSynchronizationContext : SynchronizationContext, IDisposable
{
/// <summary>The queue of work items.</summary>
private readonly AsyncProducerConsumerQueue<KeyValuePair<SendOrPostCallback, object>> syncQueue =
new AsyncProducerConsumerQueue<KeyValuePair<SendOrPostCallback, object>>();
private readonly Thread thread = null;
public ThreadSynchronizationContext()
{
Debug.WriteLine("------------------------");
Debug.WriteLine("thread " + Thread.CurrentThread.ManagedThreadId + " - starting worker thread sync context!");
Debug.WriteLine("------------------------");
// using this hack so the new thread will start running before this function returns
using (var hack = new ManualResetEvent(false))
{
thread = new Thread(async obj =>
{
SetSynchronizationContext(obj as SynchronizationContext);
hack.Set();
try
{
Debug.WriteLine("thread " + Thread.CurrentThread.ManagedThreadId + " - awaiting queue available...");
while (await syncQueue.OutputAvailableAsync())
{
Debug.WriteLine("awaiting queue item...");
var workItem = await syncQueue.TryDequeueAsync();
Debug.WriteLine("thread " + Thread.CurrentThread.ManagedThreadId + " - queue item received!");
if (workItem.Success)
{
workItem.Item.Key(workItem.Item.Value);
}
}
Debug.WriteLine("thread " + Thread.CurrentThread.ManagedThreadId + " - queue finished :(");
}
catch (ObjectDisposedException e)
{
Debug.WriteLine("thread " + Thread.CurrentThread.ManagedThreadId + " - queue exception :((");
}
});
thread.Start(this);
hack.WaitOne();
Debug.WriteLine("worker thread: " + WorkerThreadId);
}
}
public int WorkerThreadId { get { return thread.ManagedThreadId; } }
public void Dispose()
{
syncQueue.Dispose();
}
/// <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 async override void Post(SendOrPostCallback d, object state)
{
if (d == null) throw new ArgumentNullException("d");
Debug.WriteLine("thread " + Thread.CurrentThread.ManagedThreadId + " - enqueuing item...");
await syncQueue.EnqueueAsync(new KeyValuePair<SendOrPostCallback, object>(d, state));
Debug.WriteLine("thread " + Thread.CurrentThread.ManagedThreadId + " - item enqueued.");
}
/// <summary>Dispatches a synchronous 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 Send(SendOrPostCallback d, object state)
{
if (d == null) throw new ArgumentNullException("d");
using (var handledEvent = new ManualResetEvent(false))
{
Post(SendOrPostCallback_BlockingWrapper, Tuple.Create(d, state, handledEvent));
Debug.WriteLine("thread " + Thread.CurrentThread.ManagedThreadId + " - waiting for blocking wrapper!");
handledEvent.WaitOne();
Debug.WriteLine("thread " + Thread.CurrentThread.ManagedThreadId + " - blocking wrapper finished.");
}
}
private static void SendOrPostCallback_BlockingWrapper(object state)
{
var innerCallback = (state as Tuple<SendOrPostCallback, object, ManualResetEvent>);
try
{
Debug.WriteLine("thread " + Thread.CurrentThread.ManagedThreadId + " - call callback from blocking wrapper...");
innerCallback.Item1(innerCallback.Item2);
Debug.WriteLine("thread " + Thread.CurrentThread.ManagedThreadId + " - blocking wrapper callback finished.");
}
finally
{
Debug.WriteLine("thread " + Thread.CurrentThread.ManagedThreadId + " - setting handle from blocking wrapper!");
innerCallback.Item3.Set();
}
}
}
问题:
当我启动应用程序,并将一些代表发布到上下文时,这是输出:
------------------------
thread 1 - starting worker thread sync context!
------------------------
thread 17 - awaiting queue available...
worker thread: 17
thread 1 - enqueuing item...
thread 8 - enqueuing item...
thread 8 - item enqueued.
thread 1 - item enqueued.
thread 1 - waiting for blocking wrapper!
基本上,程序在handledEvent.WaitOne();
方法的Send()
行冻结,好像队列从未开始处理添加的项目。
我有点难过,任何指导都很感激。
答案 0 :(得分:2)
这里的问题有点棘手,但有一个很好的线索,当你只调用{{1}时,你会看到你的“入队项......”调试输出两次 } 一次。
实际发生的是自定义同步上下文由线程主委托中的Send
选取。因此,它会尝试将其队列处理代码排队到自己的队列中。
要打破它:
await
行。await syncQueue.OutputAvailableAsync()
的实例)注册其继续,然后返回(导致线程退出)。ThreadSynchronizationContext
时,它会将一个项目排入队列,这会导致Send
完成。OutputAvailableAsync
继续执行捕获的Post
。如果您需要单线程同步上下文,那么您根本不应该有一个异步线程委托。相反,只需使用同步API:
ThreadSynchronizationContext
事实上,我建议完全避免thread = new Thread(obj =>
{
SetSynchronizationContext(obj as SynchronizationContext);
hack.Set();
try
{
Debug.WriteLine("thread " + Thread.CurrentThread.ManagedThreadId + " - awaiting queue available...");
while (true)
{
Debug.WriteLine("awaiting queue item...");
var workItem = syncQueue.TryDequeue();
Debug.WriteLine("thread " + Thread.CurrentThread.ManagedThreadId + " - queue item received!");
if (!workItem.Success)
break;
workItem.Item.Key(workItem.Item.Value);
}
Debug.WriteLine("thread " + Thread.CurrentThread.ManagedThreadId + " - queue finished :(");
}
catch (ObjectDisposedException e)
{
Debug.WriteLine("thread " + Thread.CurrentThread.ManagedThreadId + " - queue exception :((");
}
});
,所以我建议同时使用async void
同步方法(它仍然是“异步”的,因为它没有执行Post
立即委托;它同步入队):
SendOrPostCallback
或者,您可以省去所有这些的痛苦,只需使用已经属于AsyncEx的the AsyncContextThread
type。 public override void Post(SendOrPostCallback d, object state)
{
if (d == null) throw new ArgumentNullException("d");
Debug.WriteLine("thread " + Thread.CurrentThread.ManagedThreadId + " - enqueuing item...");
syncQueue.Enqueue(new KeyValuePair<SendOrPostCallback, object>(d, state));
Debug.WriteLine("thread " + Thread.CurrentThread.ManagedThreadId + " - item enqueued.");
}
在内部使用自己的单线程同步上下文。