SynchronizationContext Send() должен быть тем же потоком?
У меня есть этот сценарий, в котором я пытаюсь обработать событие в том же потоке, в котором оно было создано. Что обычно делается в UiThread, но я не в UiThread для начала. У меня есть тест с в основном следующими шагами. Я пропустил некоторые детали. Я не совсем уверен, должно ли это действовать так, как мне кажется.
Сначала я проверяю идентификатор текущей темы
var myThreadId = Thread.CurrentThread.ManagedThreadId;
Я создаю SynchronizationContext и устанавливаю как текущий
var _context = new SynchronizationContext();
SynchronizationContext.SetSynchronizationContext(_context);
Затем я отправляю некоторые действия в контекст (мы сейчас находимся в другом потоке)
_context.Send(x => _action(sender, e), null);
Внутри этого действия я снова проверяю ThreadId
Assert.Equal(myThreadId, Thread.CurrentThread.ManagedThreadId);
Это не удается. Разве я не должен быть снова в моей первоначальной теме?
2 ответа
Создание нового SynchronizationContext
и используя Send
или же Post
точно так же, как синхронный вызов делегата, как если бы вы делали это самостоятельно. Код довольно прост (взят из источника):
public virtual void Send(SendOrPostCallback d, Object state)
{
d(state);
}
Вы пытаетесь имитировать действие пользовательских контекстов, таких как DispatcherSynchronizationContext
например, который знает о потоке цикла сообщений пользовательского интерфейса WPF. Такое поведение не происходит здесь.
Если вы пришли из потока пользовательского интерфейса, вам нужно захватить контекст и передать его.
Вы можете увидеть это более четко внутри DispatcherSynchronizationContext
какие очереди работают с пользовательским интерфейсом, используя Dispatcher
учебный класс:
/// <summary>
/// Synchronously invoke the callback in the SynchronizationContext.
/// </summary>
public override void Send(SendOrPostCallback d, Object state)
{
// Call the Invoke overload that preserves the behavior of passing
// exceptions to Dispatcher.UnhandledException.
if(BaseCompatibilityPreferences.GetInlineDispatcherSynchronizationContextSend() &&
_dispatcher.CheckAccess())
{
// Same-thread, use send priority to avoid any reentrancy.
_dispatcher.Invoke(DispatcherPriority.Send, d, state);
}
else
{
// Cross-thread, use the cached priority.
_dispatcher.Invoke(_priority, d, state);
}
}
Если вы создаете новый SynchronizationContext
, он всегда будет оборачивать пул потоков и никогда не выполнится Send
или же Post
в потоке пользовательского интерфейса.
Класс SynchronizationContext является базовым классом, который предоставляет контекст с бесплатными потоками без синхронизации.
Например;
void Button_Click(object sender, EventArgs e)
{
var context = SynchronizationContext.Current;
// this is executred on the UI thread.
context.Send(() =>
{
// this is also executed on the UI thread.
});
Task.Run(() =>
{
// this is executed on a worker thread
context.Send(() =>
{
// this is still executed on the UI thread!
});
}
// what you are doing will always execute on a worker thread.
var myNewContext = new SynchronizationContext();
SynchronizationContext.SetSynchronizationContext(myNewContext);
myNewContext.Send(() =>
{
// this will run on a worker thread.
}
}