C# async/await 误解不同的调用变体

问题描述 投票:0回答:1

为了更好地理解

C#
中的异步编程,我制作了以下函数:

async Task Do()
{
  Console.WriteLine("Do Start 1");
  Console.WriteLine("Do Start 2\n");
  await Task.Delay(3500);
  Console.WriteLine("Do 1");
  Console.WriteLine("Do 2\n");
}

async Task Foo()
{
  Console.WriteLine("Foo Start 1");
  Console.WriteLine("Foo Start 2\n");
  await Task.Delay(1500);
  Console.WriteLine("Foo 1");
  Console.WriteLine("Foo 2\n");
}

async Task Bar()
{
  Console.WriteLine("Bar Start 1");
  Console.WriteLine("Bar Start 2\n");
  await Task.Delay(2500);
  Console.WriteLine("Bar 1");
  Console.WriteLine("Bar 2\n");
}

这两种函数调用的变体工作原理相同:

// I variant
Do();
Foo();
Bar();

// II variant
var one = Do();
var two = Foo();
var three = Bar();

// main thread
for (int i = 1; i <= 3; i++)
{
  await Task.Delay(900);
  Console.WriteLine($"Main({i})");
}

Console.ReadLine();
// Output:
//   Do Start 1
//   Do Start 2    

//   Foo Start 1
//   Foo Start 2    

//   Bar Start 1
//   Bar Start 2    

//   Main(1)
//   Foo 1
//   Foo 2    

//   Main(2)
//   Bar 1
//   Bar 2    

//   Main(3)
//   Do 1
//   Do 2

第一个问题:这两个变体的工作原理相同,但

Visual Studio IDE
警告第一个变体需要更改:
Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call
。为什么只有第一个变体而不是第二个?输出良好,主线程没有阻塞。为什么我无法在没有
async
关键字的情况下调用
await
函数?

III 瓦里纳特。主线程被阻塞。我知道如果我在其他函数的主体中调用这些函数,但在这种情况下,当我在主线程(Program.cs)中调用它们时,请告诉我它与 I 和 II 变体相比有什么优势?

// III variant
var one = Do();
var two = Foo();
var three = Bar();

// a
await one;
await two;
await three;

// b. the same
// await Task.WhenAll(one, two, three);

// main thread
// for() ...

// Output:
//   Do Start 1
//   Do Start 2    

//   Foo Start 1
//   Foo Start 2    

//   Bar Start 1
//   Bar Start 2    

//   Foo 1
//   Foo 2    

//   Bar 1
//   Bar 2    

//   Do 1
//   Do 2    

//   Main(1)
//   Main(2)
//   Main(3)

IV 变体。代码同步工作。我知道在某些情况下需要它,但如果您告诉我更多有关它的用例的信息,我很高兴:

// IV variant
await Do();
await Foo();
await Bar();

// main thread
// for() ...

// Output:
//   Do Start 1
//   Do Start 2

//   Do 1
//   Do 2

//   Foo Start 1
//   Foo Start 2

//   Foo 1
//   Foo 2

//   Bar Start 1
//   Bar Start 2

//   Bar 1
//   Bar 2

//   Main(1)
//   Main(2)
//   Main(3)
c# asynchronous async-await
1个回答
1
投票

IDE 警告您应该这样做的原因

await Do();

而不仅仅是

Do();

因为这通常是一个很容易犯的错误。更难犯这样的错误

var result = await Do();

var result = Do();

因为想必您想在某个地方使用

result
,对吧?如果你这样做,那么你很快就会意识到,如果没有
await
,它的类型是错误的。如果您不使用它,那么 IDE 无论如何都会警告您有关未使用的变量。

IV 变体。代码同步工作

不,它不是同步的。它是顺序的。一系列操作

async Task Fire()
{
    await A();
    await B();
    await C();
}

将依次运行。但它并不能阻止其他代码在其间运行。特别是这些电话:

var t1 = Task.Run(Fire);
var t2 = Task.Run(Fire);
var t3 = Task.Run(Fire);
await Task.WhenAll(t1, t2, t3);

将并行调用

Fire()
3 次。但每个
Fire()
都会依次调用自己的调用。

通过这种设计,我们可以简单地创建一个复杂的作业,其中有些事情并行发生,而有些事情一个接一个地发生。

我理解在某些情况下是需要的

您想说“几乎所有情况”。无论您编写什么程序,它始终是让事情以具体、确定性的方式发生的理想属性。如果调用相互依赖,则它们必须是连续的,但即使它们不依赖,也最好按照固定的顺序一一进行调用。它使调试、推理代码变得更加容易。我们并行生成事物的唯一原因是出于性能原因。

© www.soinside.com 2019 - 2024. All rights reserved.