Короче говоря, я реализовал класс, производный от SynchronizationContext, чтобы приложениям с графическим интерфейсом было проще получать события, возникающие в потоках, отличных от потока GUI. Я был бы очень признателен за комментарии по моей реализации. В частности, есть ли что-то, против чего вы бы порекомендовали, или это может вызвать проблемы, которые я не предвидел? Мои начальные тесты прошли успешно.
Длинная версия:
В настоящее время я занимаюсь разработкой бизнес-уровня распределенной системы (WCF), которая использует обратные вызовы для передачи событий с сервера на клиенты. Одна из моих целей разработки - предоставить привязываемые бизнес-объекты (например, INotifyPropertyChanged / IEditableObject и т. Д.), Чтобы их было легко использовать на стороне клиента. В рамках этого я предоставляю реализацию интерфейса обратного вызова, который обрабатывает события по мере их поступления, обновляет бизнес-объекты, которые, в свою очередь, вызывают события изменения свойств. Поэтому мне нужно, чтобы эти события возникали в потоке графического интерфейса (чтобы избежать исключений между операциями). Отсюда моя попытка предоставить пользовательский SynchronizationContext, который используется классом, реализующим интерфейс обратного вызова, для распространения событий в поток GUI. Кроме того, я хочу, чтобы эта реализация была независимой от клиентской среды - например, приложение WinForms GUI или ConsoleApp или что-то еще. Другими словами, я не хочу предполагать, что статический SynchronizationContext.Current доступен. Отсюда мое использование ExecutionContext в качестве запасной стратегии.
public class ImplicitSynchronisationContext : SynchronizationContext
{
private readonly ExecutionContext m_ExecContext;
private readonly SynchronizationContext m_SyncContext;
public ImplicitSynchronisationContext()
{
// Default to the current sync context if available.
if (SynchronizationContext.Current != null)
{
m_SyncContext = SynchronizationContext.Current;
}
else
{
m_ExecContext = ExecutionContext.Capture();
}
}
public override void Post(SendOrPostCallback d, object state)
{
if (m_SyncContext != null)
{
m_SyncContext.Post(d, state);
}
else
{
ExecutionContext.Run(
m_ExecContext.CreateCopy(),
(object args) =>
{
ThreadPool.QueueUserWorkItem(new WaitCallback(this.Invoker), args);
},
new object[] { d, state });
}
}
public override void Send(SendOrPostCallback d, object state)
{
if (m_SyncContext != null)
{
m_SyncContext.Send(d, state);
}
else
{
ExecutionContext.Run(
m_ExecContext.CreateCopy(),
new ContextCallback(this.Invoker),
new object[] { d, state });
}
}
private void Invoker(object args)
{
Debug.Assert(args != null);
Debug.Assert(args is object[]);
object[] parts = (object[])args;
Debug.Assert(parts.Length == 2);
Debug.Assert(parts[0] is SendOrPostCallback);
SendOrPostCallback d = (parts[0] as SendOrPostCallback);
d(parts[1]);
}
}