【问题标题】:Changing a synchronous method to async将同步方法更改为异步
【发布时间】:2016-01-07 01:00:22
【问题描述】:

我在 Google 上搜索了很多,并阅读了不同的新手教程,但我认为我不明白应该做什么。基本上,如果服务器启动并运行,则存在同步的现有代码。有时,很少,服务器需要更长的时间才能启动,所以我想将它包装在一些重试逻辑中。我构建了一个完全愚蠢的控制台应用程序来尝试了解 async 和 await 是如何工作的,然后想出了这个:

    private static int counter = 0;

    static void Main(string[] args)
    {
        DoIt();
        Console.ReadLine();
    }

    static bool LongTask()
    {
        if (counter == 2)
        {
            Console.WriteLine("finally true");
            Thread.Sleep(1000);
            return true;
        }
        counter++;
        Console.WriteLine("false");
        Thread.Sleep(1000);
        return false;
    }

    public static Task<bool> WrapperLongTask()
    {
        Console.WriteLine("wrapper called");
        return Task.Run(() => LongTask());            
    }

    public static async Task DoIt()
    {
        Console.WriteLine("hi");
        var result = await WrapperLongTask();
        while (result != true)
        {
            result = await WrapperLongTask();
            Console.WriteLine("inside while loop");
        }
        Console.WriteLine($"outside while loop {result}");
        Console.WriteLine("bye");
    }

我的 LongTask 函数代表了我当前的函数,该函数通常第一次就可以正常工作。然后用

调用这个方法可以吗
Task.Run(() => LongTask())

假设那是“好的”,那么我基本上会在我的实际代码中为我当前的方法 DoWork() 创建它。

Task DoWorkAsync(....) {
     return Task.Run(() => DoWork()
}

基本上只是将它包装在 Task.Run 中,将返回类型更改为 Task。然后当我稍后调用这个方法时,我会做类似的事情

var x = await DoWorkAsync;
// do more stuff with x

这样我应该将以前的同步方法异步转换吗?

编辑

DoWork 的伪代码(字符串目录,CancellationToken 令牌)

var files = Directory.GetFiles(directory, "*", SearchOption.AllDirectories);
foreach (var file in files) {
    try {
       token.ThrowIfCancellationRequested();
       var fileName = Path.GetFileName(file);
       // check if file already exists on server, if not, upload it
    }
    catch (Exception exception) {
       // error handling
    }
}

【问题讨论】:

  • 这是什么“做某事”?在服务器执行任何进一步操作之前,您是否必须等待结果?它是否调用任何具有异步等效项的方法?服务器还在做什么? (这是 ASP.NET Web 应用程序还是其他)?根据这些问题的答案,尤其是最后一个问题,我可能会提出不同的建议,或者您可能根本不关心异步。
  • 您能否发布您的“真实”代码,以便我们了解它是如何实现异步的?

标签: c# asynchronous


【解决方案1】:

简短的回答是不,你不能简单地通过使用Task.Run 包装操作并使方法返回Task 来将所有类型的同步代码转换为异步代码。

通常,当考虑中的操作可能会调用一些 IO 操作(文件系统读/写、网络或 Web 访问、数据库访问......等)时,异步代码才有意义。

例如,如果您有一个使用同步方法(如FileStream.Read)从文件中读取一些数据的方法,然后对此类文件的内容执行一些 CPU 工作,那么您可以通过使它调用FileStream.ReadAsync,然后异步等待ReadAsync 使用await 关键字完成,然后处理文件的内容(当然,您必须更改方法以返回Task 并成为@ 987654328@).

这种情况的好处是没有线程等待 IO 操作完成,而且线程开销很大。

没有等待 IO 操作完成的线程的好处在 ASP.NET 网站等服务器应用程序中非常重要,在这些应用程序中您期望有大量的同时请求。但是,对于简单的应用程序,您可能一开始就不想使用异步代码。

如果您想在多个 CPU 内核上运行多个 CPU 密集型操作,您可以使用 Task.Run

例如,如果您有 4 个 CPU 内核,则通过 Task.Run 创建 4 个任务来处理一些数据是有意义的。考虑前面的例子,异步等待ReadAsync完成后,可以将读取的结果拆分为4份(假设数据比较大),通过Task.Run创建4个任务,每份处理一个结果的一部分。然后,您可以使用 Task.WhenAll 异步等待 4 个任务完成。

【讨论】:

  • 如果您在大型序列上执行 CPU 密集型工作,使用 Parallel.ForEach 比使用任务更有意义。它会比“将序列分成N部分”更好地进行输入分区和负载平衡
  • 没错。我使用了Task.Run,因为我想给 OP 一个什么时候可以使用它的例子。另请注意,(AFAIK)您不能异步等待Parallel.ForEach 完成。它阻塞当前线程,直到所有操作完成。
猜你喜欢
  • 2011-10-21
  • 1970-01-01
  • 1970-01-01
  • 2019-01-14
  • 1970-01-01
  • 2021-04-12
  • 2018-03-30
  • 2019-12-26
  • 2020-12-19
相关资源
最近更新 更多