Это потому, что ключевые слова - это просто синтаксический сахар для чего-то, называемого coroutines.
Для поддержки создания асинхронных методов нет специальных инструкций IL. Вместо этого метод async можно рассматривать как своего рода конечный автомат.
Я постараюсь сделать этот пример как можно короче:
[TestClass]
public class AsyncTest
{
[TestMethod]
public async Task RunTest_1()
{
var result = await GetStringAsync();
Console.WriteLine(result);
}
private async Task AppendLineAsync(StringBuilder builder, string text)
{
await Task.Delay(1000);
builder.AppendLine(text);
}
public async Task<string> GetStringAsync()
{
// Code before first await
var builder = new StringBuilder();
var secondLine = "Second Line";
// First await
await AppendLineAsync(builder, "First Line");
// Inner synchronous code
builder.AppendLine(secondLine);
// Second await
await AppendLineAsync(builder, "Third Line");
// Return
return builder.ToString();
}
}
Это некоторый асинхронной код, как вы, вероятно, привыкли к: Наш метод GetStringAsync
сначала создает StringBuilder
синхронно, то он ждет некоторые асинхронные методы и, наконец, возвращает результат. Как это будет реализовано, если не было ключевого слова await
?
Добавьте следующий код в AsyncTest
класс:
[TestMethod]
public async Task RunTest_2()
{
var result = await GetStringAsyncWithoutAwait();
Console.WriteLine(result);
}
public Task<string> GetStringAsyncWithoutAwait()
{
// Code before first await
var builder = new StringBuilder();
var secondLine = "Second Line";
return new StateMachine(this, builder, secondLine).CreateTask();
}
private class StateMachine
{
private readonly AsyncTest instance;
private readonly StringBuilder builder;
private readonly string secondLine;
private readonly TaskCompletionSource<string> completionSource;
private int state = 0;
public StateMachine(AsyncTest instance, StringBuilder builder, string secondLine)
{
this.instance = instance;
this.builder = builder;
this.secondLine = secondLine;
this.completionSource = new TaskCompletionSource<string>();
}
public Task<string> CreateTask()
{
DoWork();
return this.completionSource.Task;
}
private void DoWork()
{
switch (this.state)
{
case 0:
goto state_0;
case 1:
goto state_1;
case 2:
goto state_2;
}
state_0:
this.state = 1;
// First await
var firstAwaiter = this.instance.AppendLineAsync(builder, "First Line")
.GetAwaiter();
firstAwaiter.OnCompleted(DoWork);
return;
state_1:
this.state = 2;
// Inner synchronous code
this.builder.AppendLine(this.secondLine);
// Second await
var secondAwaiter = this.instance.AppendLineAsync(builder, "Third Line")
.GetAwaiter();
secondAwaiter.OnCompleted(DoWork);
return;
state_2:
// Return
var result = this.builder.ToString();
this.completionSource.SetResult(result);
}
}
Так, очевидно, код перед первым await
ключевого слова просто остается тем же самым. Все остальное преобразуется в конечный автомат, который использует операторы goto
для выполнения вашего предыдущего кода кусочно. Каждый раз, когда одна из ожидаемых задач завершается, конечный автомат переходит к следующему шагу.
Этот пример упрощен, чтобы уточнить, что происходит за кулисами. Добавьте обработку ошибок и некоторые из них: foreach
-Loops в вашем асинхронном методе, а конечный автомат становится намного сложнее.
Кстати, есть еще одна конструкция в C#, которая делает такую вещь: ключевое слово yield
. Это также создает конечный автомат, и код выглядит очень похоже на то, что производит await
.
Для дальнейшего ознакомления ознакомьтесь с this CodeProject, который более глубоко изучает сгенерированный конечный автомат.
Код в async/await основан на [Шаблон переадресации Async Джеффа Рихтера] (http://channel9.msdn.com/Blogs/Charles/Jeffrey-Richter-and-his-AsyncEnumerator). Уже есть голос, предлагающий исправить это https://ndepend.uservoice.com/forums/226344-ndepend-user-voice/suggestions/6375659-exclude-compiler-generated-code-by-default. – Aron
Так как работает async/await. То, что вы видите, - это [деталь реализации] (http://msdn.microsoft.com/en-us/magazine/hh456402.aspx) этой функции. –
Это действительно подчеркивает важность того, чтобы вы не слепо использовали асинхронный доступ по всему миру, когда он не нужен. Это больше накладных расходов, чем я предполагаю, я ожидал. –