Ваше ожидание неверно, потому что нет общего способа «внедрить» делегат в работающий поток. Ваш «первый поток» был запущен в тестовом прогоне, выполнит один или несколько тестов, а затем остановится - нет способа прервать его и сказать ему запустить CallbackInFirstThread
. Класс SynchronizationContext
запускает делегаты с Post
в пуле потоков, потому что это единственная опция, которую он имеет.
Производные классы, такие как WindowsFormsSynchronizationContext
, используют цикл сообщений в приложениях WinForms для передачи делегата с Post
в поток пользовательского интерфейса, но в тестовом модуле нет эквивалента.
Если вы хотите проверить, какой SynchronizationContext
код, который вы тестируете, вы можете создать свой собственный производный класс, который устанавливает флаг, который вы можете проверить в своем тесте. Вот пример:
public class TestSynchronizationContext : SynchronizationContext
{
[ThreadStatic]
private static object _CurrentPostToken;
/// <summary>
/// Gets the context's token, if the current thread is executing a delegate that
/// was posted to this context; otherwise, null.
/// </summary>
public static object CurrentPostToken
{
get
{
return _CurrentPostToken;
}
}
public object Token { get; private set; }
/// <summary>
/// Gets a WaitHandle that is set after the context executes a posted delegate.
/// </summary>
public AutoResetEvent PostHandle { get; private set; }
public TestSynchronizationContext(object token)
{
Token = token;
PostHandle = new AutoResetEvent(false);
}
public override void Post(SendOrPostCallback d, object state)
{
try
{
_CurrentPostToken = Token;
// Execute the callback on this thread, so that we can reset the context
// when it's finished.
d(state);
}
finally
{
_CurrentPostToken = null;
}
// The test method will wait on this handle so that it doesn't exit before
// the synchronization context is called.
PostHandle.Set();
}
}
В StartWorkInFirstThread
установите контекст для экземпляра TestSynchronizationContext
:
SynchronizationContext.SetSynchronizationContext(
new TestSynchronizationContext(new object()));
После того, как вы позвоните BeginInvoke
, вам нужно дождаться, пока Post
произойдет, прежде чем выйти из теста, поэтому позвоните:
((TestSynchronizationContext)SynchronizationContext.Current).PostHandle.WaitOne(1000);
В CallbackInFirstThread
вы можете проверить, какой контекст используется с чем-то вроде:
Assert.IsNotNull(TestSynchronizationContext.CurrentPostToken);
Суть в том, что не существует простого способа на самом деле отправить обратно в первый поток, но вы можете проверить, что используется правильный контекст, так что, когда ваш код выполняется в реальном приложении, обратный вызов будет выполняться в Пользовательский интерфейс.