Всегда ли ConfigureAwait(true) возвращается в исходный поток, даже если метод вызываемого объекта внутренне выполняет ConfigureAwait(false)?
Я ожидал вернуться к теме № 1 в местоположении 1.2, но не сделал этого. Есть ли способ вернуться к потоку пользовательского интерфейса после выполнения асинхронного вызова? Спасибо
Также я не могу сделать метод верхнего уровня асинхронным. Не уверен, что Async полностью решит эту проблему, но сейчас у меня нет такого выбора.
class Program
{
static void Main(string[] args)
{
ComputeThenUpdateUI().Wait();
}
static async Task ComputeThenUpdateUI()
{
Console.WriteLine($"1.1 {Thread.CurrentThread.ManagedThreadId}");
await ExpensiveComputingNonUI().ConfigureAwait(true);
Console.WriteLine($"1.2 {Thread.CurrentThread.ManagedThreadId}");
}
static async Task ExpensiveComputingNonUI()
{
Console.WriteLine($"2.1 {Thread.CurrentThread.ManagedThreadId}");
await Task.Delay(3000).ConfigureAwait(false);
Console.WriteLine($"2.2 {Thread.CurrentThread.ManagedThreadId}");
await Task.Delay(3000).ConfigureAwait(false);
Console.WriteLine($"2.3 {Thread.CurrentThread.ManagedThreadId}");
}
}
Output:
1.1 1
2.1 1
2.2 4
2.3 4
1.2 4
2 ответа
Re: не .ConfigureAwait(true)
означает, что поток вернется в тот же поток после завершения ожидания?
Нет, в большинстве случаев нет никаких гарантий. Стивен Клири имеет однозначный ответ:
Этот "контекст" является SynchronizationContext.Current, если он не равен нулю, и в этом случае это TaskScheduler.Current. (Если нет выполняемой в данный момент задачи, то TaskScheduler.Current совпадает с TaskScheduler.Default, планировщиком пула потоков).
Важно отметить, что SynchronizationContext или TaskScheduler не обязательно подразумевает определенный поток. Интерфейс SynchronizationContext пользовательского интерфейса будет планировать работу потока пользовательского интерфейса; но ASP.NET SynchronizationContext не будет планировать работу для определенного потока.
т.е. только вызовы, сделанные из потока пользовательского интерфейса, например, WPF или Windows Forms, гарантированно вернутся в тот же поток. Если вам действительно нужно вернуться к тому же потоку, вам нужно будет выполнить настройку, чтобы гарантировать продолжение продолжения в исходном потоке.
Если это не является существенным (например, для потоков пользовательского интерфейса), возврат к одному и тому же потоку обычно нежелателен, поскольку это может снизить производительность (если существует конфликт для потока) и привести к взаимоблокировкам
Re: не могу сделать основной метод верхнего уровня асинхронным
Теперь это возможно в C# 7.1:
public static async Task Main(string[] args)
{
// Can await asynchronous tasks here.
}
ConfigureAwait
это локальное решение, в каждом методе, который вы пишете. ConfigureAwait(true)
это способ указать, что вы хотите попытаться вернуться в тот же контекст, в котором был запущен ваш метод при его первом вводе.
Во-первых, это означает, что если вы не знаете, в каком контексте вы изначально работали, то нет смысла пытаться вернуться в тот же контекст. Вы не должны пытаться делать что-либо, зависящее от контекста, если вас могут вызывать в разных контекстах. Вот почему это обычно хороший совет для использования ConfigureAwait(false)
если вы создаете библиотеку. Вы не будете знать контексты, в которых вызывается ваш код.
А во-вторых, это означает, что нет "глобального" контекста, к которому можно вернуться. Если ваш абонент (или любой из его вызывающих абонентов и т. Д. Выше по цепочке) уже отделился от своего первоначального контекста вызова, нет способа (с помощью этого механизма) вернуться в этот контекст.
Если вы хотите переключиться на определенный контекст (например, поток пользовательского интерфейса), и это не обязательно будет вашим вызывающим контекстом, вам нужно использовать другой, зависящий от контекста механизм, такой как Dispatcher.Invoke()
или что угодно в вашей среде.