在C#控制台应用程序中使用“async”

11 浏览
0 Comments

在C#控制台应用程序中使用“async”

我有这个简单的代码:

public static async Task SumTwoOperationsAsync()
{
    var firstTask = GetOperationOneAsync();
    var secondTask = GetOperationTwoAsync();
    return await firstTask + await secondTask;
}
private async Task GetOperationOneAsync()
{
    await Task.Delay(500); // 仅仅是为了模拟一个耗时的操作
    return 10;
}
private async Task GetOperationTwoAsync()
{
    await Task.Delay(100); // 仅仅是为了模拟一个耗时的操作
    return 5;
}

很好。这个代码编译通过。

但是假设我有一个控制台应用程序,并且我想运行上面的代码(调用`SumTwoOperationsAsync()`)。

static void Main(string[] args)
{
     SumTwoOperationsAsync();
}

但是我读到了(当使用`sync`时)我必须在上下两个方向上同步:

这是否意味着我的`Main`函数应该标记为`async`?

嗯,它不行,因为会有编译错误:

一个入口点不能标记为“async”修饰符

如果我理解异步的东西,线程将进入`Main`函数 → `SumTwoOperationsAsync` → 调用两个函数并退出。但在`SumTwoOperationsAsync`之前

我错过了什么吗?

0
0 Comments

在C#的控制台应用程序中使用"async"关键字的原因是希望在主线程中执行异步操作。然而,使用"async"关键字时会出现问题,因为控制台应用程序的入口点是Main方法,而Main方法不能是异步的。

为了解决这个问题,可以通过创建一个辅助方法MainAsync来实现异步操作,并在Main方法中调用该方法。具体的做法是将Main方法修改为静态的,并在其中创建一个Task对象t,将MainAsync方法作为参数传递给该对象。然后,使用t.Wait()方法来等待异步操作的完成。

下面是实现的代码示例:

static void Main(string[] args)
{
    Task t = MainAsync(args);
    t.Wait();
}
static async Task MainAsync(string[] args)
{
    // 执行异步操作的代码
}

通过这种方式,我们可以在控制台应用程序中使用"async"关键字,并在主线程中执行异步操作。这样就可以避免由于Main方法不能是异步的而导致的问题。

0
0 Comments

在大多数项目类型中,您的async“上”和“下”将在async void事件处理程序结束或返回一个Task到您的框架中。

然而,控制台应用程序不支持这样做。

您可以在返回的任务上直接使用Wait:

static void Main()
{
  MainAsync().Wait();
  // or, if you want to avoid exceptions being wrapped into AggregateException:
  //  MainAsync().GetAwaiter().GetResult();
}
static async Task MainAsync()
{
  ...
}

或者您可以使用自己的上下文,例如我写的这个:

static void Main()
{
  AsyncContext.Run(() => MainAsync());
}
static async Task MainAsync()
{
  ...
}

更多关于async控制台应用程序的信息可以在我的博客上找到。

如果不是控制台应用程序,那么我的观察结果是正确的吗?线程在SumTwoOperationsAsync停止了...对吗?(假设主调用方法没有标记为async)

我在我的博客上有一篇介绍如何使用async的文章,通过"上下文"来解释线程是如何工作的。

重新阅读您的答案-您是说在控制台应用程序中使用async是没有意义的吗?

:不是。虽然不太常见,但除非您正在使用仅支持async的API,否则这是没什么问题的。控制台(和其他桌面)应用程序通常不太关心浪费线程的问题。

如果我不想添加第二个MainAsync方法,那么这与Task.Run( async () => { ... async calls ... } ).Wait();一样吗?

:您可以使用Task.Run(..).GetAwaiter().GetResult();,它将阻塞主线程直到异步工作完成。AsyncContext.Run是不同的;它在调用线程上运行一个“主循环”,并在单线程上执行所有异步工作。

第一种方法似乎不能很好地处理异常。抛出的异常会错过周围的try/catch块。至少在我的应用程序中会崩溃,原因不明...

:不,它保证会在catch中捕获异常。如果您没有看到这种情况,请提出自己的问题,并提供一个最小的代码示例。

我的错。异常被捕获了,只是我的异常处理代码抛出了新的异常。

值得一提的是,处理根跳板上的异常的另一种选择是AppDomain.CurrentDomain.UnhandledException += (o, e) => { /* ... */ };。

如果Stephen的评论不太清楚(我花了一点时间才明白...),区别在于,虽然Task.Run(..).GetAwaiter().GetResult()会从任务池中分配一个新的任务(可能会启动新线程或其他资源)来运行应用程序,然后讽刺的是,它会浪费当前执行的线程来阻塞刚刚创建的任务,而Stephen的AsyncContext更有效地利用了您隐含提供的线程-即您当前在Main中执行的线程-来完成这个任务。

MainAsync().GetAwaiter().GetResult();您太棒了!

0
0 Comments

原因:在控制台应用程序中使用'async'关键字时,使用Task.Result或Task.Wait会导致调用线程被阻塞,从而无法提高可扩展性。

解决方法:避免使用Task.Result和Task.Wait,可以使用异步方式来避免阻塞调用线程。

0