【问题标题】:Async/Await - not understanding why one way is Blocking and another is not异步/等待 - 不明白为什么一种方式是阻塞而另一种方式不是
【发布时间】:2015-06-08 19:25:49
【问题描述】:

仍在尝试围绕 async/await 解决问题。我有以下拖放加载方法:

    private async void p_DragDrop(object sender, DragEventArgs e)
    {
        string[] files = (string[])e.Data.GetData(DataFormats.FileDrop);

        List<string> CurvesToLoad = new List<string>();
        List<string> TestsToLoad = new List<string>();

        foreach (string file in files)
        {
            if (file.ToUpper().EndsWith(".CCC"))
                CurvesToLoad.Add(file);
            else if (file.ToUpper().EndsWith(".TTT"))
                TestsToLoad.Add(file);
        }

        //SNIPPET IN BELOW SECTION
        foreach (string CurvePath in CurvesToLoad)
        {
            Curve c = new Curve(CurvePath);

            await Task.Run(() =>
            {
                c.load();
                c.calculate();
            });

            AddCurveControls(c);
        }
        //END SNIPPET

        foreach (string TestPath in TestsToLoad)
        {
            Test t = new Test(TestPath);

            await Task.Run(() =>
            {
                t.load();
            });

            AddTestControls(t);
        }
    }

正如我预期的那样,它是非阻塞的 - 我能够在加载多个项目时在 TabControl 的选项卡之间导航,并且我可以看到每个选项卡在完成加载时弹出。

然后我尝试转换为:

    private Task<Curve> LoadAndCalculateCurve(string path)
    {
        Curve c = new Curve(path);

        c.load();
        c.calculate();

        return Task.FromResult(c);
    }

然后将第一个代码块中标记的sn-p替换为:

    foreach (string CurvePath in CurvesToLoad)
    {
        Curve c = await LoadAndCalculateCurve(CurvePath);
        AddCurveControls(c);
    }

它变成了阻塞 - 我无法在加载时浏览选项卡,然后所有加载的项目在完成后立即出现。只是想在这里学习和理解其中的差异 - 非常感谢。

编辑:

更新了 LoadAndCalculateCurve():

    private async Task<Curve> LoadAndCalculateCurve(string path)
    {
        Curve c = new Curve(path);

        await Task.Run(() => {
            c.load();
            c.calculate();
        });

        return c;
    }

【问题讨论】:

  • 您应该区分两种类型的任务 - IO 绑定和 CPU 绑定。它们中的每一个都有不同的性质,应该以不同的方式处理。
  • Task.FromResult 返回已完成的任务,因此该部分代码(通常)不涉及其他线程。

标签: c# async-await


【解决方案1】:

异步方法不会在不同的线程中执行,await 不会启动线程。 async 仅启用 await 关键字,await 等待已运行的内容。

所有这些代码都在 UI 线程上运行。

【讨论】:

    【解决方案2】:

    所以基本上这就是我所知道的情况。

    在你编写的第一个代码中

        foreach (string CurvePath in CurvesToLoad)
        {
            Curve c = new Curve(CurvePath);
    
            await Task.Run(() =>
            {
                c.load();
                c.calculate();
            });
    
            AddCurveControls(c);
        }
    

    这会按预期执行异步流程,因为您正在使用 Task.Run 将工作添加到 ThreadPool 队列。

    在您的第二次尝试中,您不这样做并且您正在使用相同的任务。尝试在第二次尝试中使用与 (Task.Run) 相同的逻辑,我认为它会起作用

    【讨论】:

      【解决方案3】:

      你的LoadAndCalculateCurve方法的实现是同步创建一个Curve,同步加载并执行计算,然后返回包裹在Task中的结果。没有什么是异步的。当您await 时,它将调用该方法,执行所有同步工作以获取(已完成)任务,然后为该(已完成)任务添加将立即触发的延续。

      当您改用Task.Run 时,您将这些长时间运行的操作安排在另一个线程中进行,并且立即返回一个(尚未完成)Task,您可以使用它来运行代码当它最终完成它的工作时。

      【讨论】:

      • 是使用Task.Run安排长时间运行的操作在另一个线程中进行的唯一方法吗?让 LoadAndCalculateCurve() 返回 void 并在 Task.Run(() => LoadAndCalculateCurve(Curve c)) 中调用它会有什么后果(如果有的话)?
      • @Worker 如果它本质上是 CPU 绑定的工作,那很好。如果它本质上是基于 IO 的工作,那么您应该生成代表该工作完成的任务,而不是使用 Task.Run
      • 我在您发表评论之前更改了 LoadAndCalculate 方法(如问题编辑中所示)并获得了所需的结果,但是由于 load() 是基于 IO 的(calculate() 是 CPU 绑定的)你能够提供一个简短的示例,说明如何生成代表完成的任务,而不是使用Task.Run
      • @Worker 它将具体到操作的实际内容。通常,您用于执行 IO 的任何 API 都会为您提供 Task,或其他一些您可以创建 Task 的固有同步机制。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-28
      • 1970-01-01
      • 1970-01-01
      • 2014-08-16
      • 2014-05-03
      • 1970-01-01
      相关资源
      最近更新 更多