【问题标题】:Can this code work as asynchronous? [closed]这段代码可以异步工作吗? [关闭]
【发布时间】:2017-08-11 15:30:39
【问题描述】:

我想运行 GetAnswerToLife 方法等待 5 秒,然后在“for 循环”打印数字时打印 210。但是下面的代码只打印了 for 循环,我不能让这个异步方法工作。 210在任何地方都看不到。我在这里有什么遗漏吗?

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApplication30
{
internal class Program
{
    private static async Task<int> GetAnswerToLife()
    {
        await Task.Delay(5000);
        int answer = 21 * 10;
        return answer;
    }

    private static void Main(string[] args)
    {
        for (int i = 0; i < 100; i++)
        {
            Console.WriteLine(i);
            Thread.Sleep(100);
        }

        int a =  GetAnswerToLife().Result;
        Console.WriteLine(a);
    }
}
}

【问题讨论】:

  • 这对我来说很好。
  • 它可以工作,但它们不能异步工作。在 for 循环完成打印之后,打印 210。我想要的循环和其他方法同时开始。
  • 您为什么希望它们同时运行?甚至在您等待 GetAnswerToLife 方法之前,for 循环就已完成。
  • 那我该怎么做才能让它们同时启动呢?
  • 不可能同时启动。一个(甚至更多)cpu 周期最小时间间隔

标签: c# async-await


【解决方案1】:

它可以工作,但它们不能异步工作。 for循环之后 完成打印,然后打印 210。我想要什么循环 和其他方法同时启动。

我认为您将异步工作与并行性混淆了。异步正在工作,但没有阻塞线程,一旦你的线程命中await,它实际上会被释放到线程池做一些其他工作,同时等待await之后的语句进行评估,只有在此之后执行才能继续.

并行性是并行完成工作的地方,因此是动作的名称。

以这个为例。

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            Parallel.Invoke(PrintLoop, async () => Console.WriteLine(await GetAnswerToLife()));
        }

        public static void PrintLoop()
        {
            for (int i = 0; i < 100; i++)
            {
                Console.WriteLine(i);
                Thread.Sleep(100);
            }
        }

        public static async Task<int> GetAnswerToLife()
        {
            await Task.Delay(5000);
            return 21 * 10;
        }
    }
}

现在 210 将打印在数字 1 和 100 之间,这是不同的,我认为这是您最初想要的。

@Stephen Cleary explanations 关于异步操作真的不在图表之列,您绝对应该检查一下作为进一步的阅读材料。

【讨论】:

  • @downvoter,你能解释一下吗
  • 感谢您的回答。这真的清除了。
  • @Lyrk 如果您有兴趣,我已经添加了进一步阅读链接。
【解决方案2】:

您在循环后调用GetAnswerToLife。显然,它会在循环之后执行。

如果您希望在循环的同时计算生命的答案,请在循环开始之前调用它:

private static async Task<int> GetAnswerToLife()
{
    await Task.Delay(5000);
    int answer = 21 * 10;

    Console.WriteLine(answer);

    return answer;
}

private static void Main(string[] args)
{
    var answering = GetAnswerToLife();

    for (int i = 0; i < 100; i++)
    {
        Console.WriteLine(i);
        Thread.Sleep(100);
    }

    // Calling .Result will ensure the answer has been calculated
    int a = answering.Result;
}

我还更改了您对生活实现的答案,因此它会在控制台中打印答案,以便在计算完成时而不是在循环结束时知道答案。

【讨论】:

  • 如果不需要结果,为什么还要计算呢? BTW Task.Result 是一个阻塞操作,只要任务完成就会返回
  • 你可以,但你不能确定生活的答案已经被计算出来了。基本上,在非异步方法中调用Result 属性就像在异步方法中调用await。老实说,我不确定这是否完全正确。可能有区别,谈论AsyncContext 和类似的东西,但我从来没有完全理解这些东西(如果你想了解更多相关信息,请查看 Stephen Cleary 的有关该主题的文章)
  • @fharreau 它与异步或非异步方法无关。对 Result 的调用将在内部调用 Wait 并且在 any 情况下会阻塞
  • @PiyushParashar 这对于控制台应用来说是特殊的,因为没有真正的同步上下文任务可以返回,所以它不会返回到调用者上下文
  • @PiyushParashar WPF/WinForms 确实具有可以捕获的真实同步上下文,因此此代码将在控制台应用程序中完美运行,但可能导致 WPF/WinForms 应用程序死锁。
猜你喜欢
  • 1970-01-01
  • 2012-11-23
  • 1970-01-01
  • 1970-01-01
  • 2021-10-03
  • 2021-01-04
  • 2011-11-24
相关资源
最近更新 更多