У меня есть код async / await, и я хочу использовать API, похожий на веб-сокет. Для получения новых сообщений требуется обратный вызов, который вызывается из другого потока.
Могу ли я выполнить этот обратный вызов в том же контексте async / await, что и инициация соединения, не прибегая к блокировке?
Я думаю, что это то, для чего нужен SynchronizationContext, но я не могу сказать, является ли он потокобезопасным. Если я регистрирую идентификатор потока, каждый обратный вызов будет в другом потоке. Если я зарегистрирую Task.CurrentId, он будет равен нулю. Я думаю, что один и тот же контекст синхронизации перемещается по разным потокам, так что это может быть нормально, но я не знаю, как это подтвердить.
// External api, the callbacks will be from multiple threads
public class Api
{
public static Connect(
Action<Connection> onConnect,
Action<Connection> onMessage)
{}
}
async Task<Connection> ConnectAsync(Action<Message> callback)
{
if (SynchronizationContext.Current == null)
{
SynchronizationContext.SetSynchronizationContext(new SynchronizationContext());
}
var syncContext = SynchronizationContext.Current;
var tcs = new TaskCompletionSource<Connection>();
// use post() to ensure callbacks from other threads are executed thread-safely
Action<Connection> onConnect = conn =>
{
syncContext.Post(o => tcs.SetResult(conn), null);
};
Action<Message> onMsg = msg =>
{
syncContext.Post(o => callback(msg), null);
};
// call the multi-threaded non async/await api supplying the callbacks
Api.Connect(onConnect, onMsg);
return await tcs.Task;
}
var connection = await ConnectAsync(
msg =>
{
/* is code here threadsafe with the send without extra locking? */
});
await connection.Send("Hello world);
SynchronizationContext
(для этого конкретного, который вы используете в своем коде, еслиSynchronizationContext.Current
имеет значение null) просто выполнит обратный вызов в потоке пула потоков, вот и все. - person Evk   schedule 17.02.2018SynchronizationContext
, который вы устанавливаете с помощьюSynchronizationContext.SetSynchronizationContext(new SynchronizationContext())
) - публикация просто отправляет обратный вызов потоку пула потоков, и все, больше ничего не делается. Так что да - обратные вызовы будут выполняться параллельно. В других контекстах все может быть иначе. - person Evk   schedule 17.02.2018