将同步方法更改为异步

Cry*_*tal 6 c# asynchronous

我在谷歌上搜索了很多并阅读了不同的菜鸟教程,但我认为我不明白正确的做法是什么。基本上,如果服务器启动并运行,现有的代码是同步的。有时,在极少数情况下,服务器需要更长的时间才能启动,因此我想将其包装在一些重试逻辑中。我构建了一个完全愚蠢的控制台应用程序来尝试了解 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");
    }
Run Code Online (Sandbox Code Playgroud)

我的 LongTask 函数代表我当前的函数,该函数通常第一次起作用。然后用这个方法调用这个方法可以吗

Task.Run(() => LongTask())
Run Code Online (Sandbox Code Playgroud)

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

Task DoWorkAsync(....) {
     return Task.Run(() => DoWork()
}
Run Code Online (Sandbox Code Playgroud)

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

var x = await DoWorkAsync;
// do more stuff with x
Run Code Online (Sandbox Code Playgroud)

这样我应该将以前的同步方法转换为异步吗?提前致谢。

编辑:

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
    }
}
Run Code Online (Sandbox Code Playgroud)

Yac*_*sad 7

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

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

例如,如果您有一个方法使用诸如 之类的同步方法从文件中读取一些数据FileStream.Read,然后对此类文件的内容进行一些 CPU 工作,那么您可以通过先调用它FileStream.ReadAsync然后异步调用来将方法转换为异步方法wait untilReadAsync通过使用await关键字完成,然后处理文件的内容(当然,您必须将方法更改为 returnTask和 be async)。

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

没有线程等待 IO 操作完成的好处在服务器应用程序(如 ASP.NET 网站)中非常重要,在这些应用程序中您预计会有大量并发请求。但是,对于简单的应用程序,您可能不想首先打扰异步代码。

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

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

  • 如果您正在对大型序列进行 CPU 密集型工作,那么使用 `Parallel.ForEach` 比使用任务更有意义。它会比“将序列分成 N 个部分”更好地进行输入分区和负载平衡 (2认同)