同步执行获取任务结果

4

我很难理解async及其相应的行为。以下是我正在尝试编写的程序模型和问题再现:

namespace AsyncTesting
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("starting....");
            MyClass myClass = new MyClass();
            myClass.DoSomeWork();
            Console.WriteLine("ending....");
            Console.ReadLine();
        }
    }

    class MyClass
    {
        public bool DoSomeWork()
        {
            return GetTrue().Result;
        }

        Task<bool> GetTrue()
        {
            return new Task<bool>(() => true);
        }
    }
}

当我调用 GetTrue().Result 时,它永远不会返回。我的 Task<bool> GetTrue() 方法是一个用于重现目的的虚拟方法,但我不确定为什么它从未返回。但如果我加上这个:

namespace AsyncTesting
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("starting....");
            MyClass myClass = new MyClass();
            myClass.DoSomeWork();
            Console.WriteLine("ending....");
            Console.ReadLine();
        }
    }

    class MyClass
    {
        public async void DoSomeWork()
        {
            await GetTrue();
        }

        Task<bool> GetTrue()
        {
            return new Task<bool>(() => true);
        }
    }
}

问题是,我希望在我的重现中保留初始行为。我不想使用 async void 来调用方法。我想要从我的“正常”方法中返回 bool 。如何实现这一点? 如何让第一个示例运行并实际从 DoSomeWork()返回 bool
提前致谢。
编辑:感谢回答。 我想问题是,我的 GetTrue()方法不是真正的重现。在我的方法中,我没有生成实际的 Task<bool>对象。它实际上是对 Task.Factory.FromAsync<bool>()的调用,该方法创建了 Task<bool>对象。而正是这个任务我不知道如何“启动”。还有其他建议吗? 谢谢!

你的编辑改变了问题的概念。如果你试图使用 task.Result 阻塞 FromAsync 的结果,你需要澄清这段代码的实际上下文。它是一个简单的控制台应用程序,还是一个 WinForms / WPF / ASP.NET 应用程序?这可能会解释为什么你遇到了死锁。请查看 Stephen Cleary 的博客:http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html - noseratio - open to work
@Noseratio 抱歉,这是一个 WCF 服务的上下文。Task.Factory.FromAsync<bool>() 调用将包装服务推送给消费者的 Begin...()End...() 函数。但我看不到任何启动任务的方法,它只是挂起了。 - user3175663
那么这仍然是客户端,您在其中调用WCF服务吗?如果是这样,客户端应用程序的类型仍然很重要。 - noseratio - open to work
@Noseratio 没错,是客户端进行 WCF 调用。客户端应用程序是 Windows Phone 8 应用程序。 - user3175663
这是一个常见的错误,我已经更新了我的答案并提供了更多详细信息。 - noseratio - open to work
2个回答

4

你可能正在寻找:

Task<bool> GetTrue()
{
    return Task.FromResult(true);
}

代替:
Task<bool> GetTrue()
{
    return new Task<bool>(() => true);
}

在后一种情况下,您甚至不会启动创建的任务。您需要使用Task.RunTask.RunSynchronously来启动它。通常您不会通过new Task()直接创建任务。请查看Stephen Toub的"Task.Factory.StartNew vs new Task(...).Start""Task.Run vs Task.Factory.StartNew"
根据您尝试实现的内容,您可能会尝试这样做:
Task<bool> GetTrue()
{
    return Task.Run(() => true);
}

然而,通常这会成为一个已知的反模式,请查看"是否应该为同步方法公开异步包装器?"更新以解决评论:

...上下文是WCF服务。Task.Factory.FromAsync()调用正在包装服务推送给使用者的Begin...()和End...()函数。但我看不到实际启动任务的方式,它只是挂起了。 ...是客户端进行WCF调用。客户端应用程序是Windows Phone 8应用程序。

您在UI线程上遇到了死锁,因为您正在阻塞一个待处理任务的结果(使用task.Result)。该任务本身无法完成,因为await继续异步发布到您的WP8应用程序的UI线程的调度程序循环中,现在已被阻止。Stephen Cleary在他的博客中详细解释了这一点。为了避免这种情况,您应该采用"async all the way down"编程模型。这实际上意味着您也应该将UI事件和命令处理程序设置为async,并在其中执行var result = await task,而不是var result = task.Result


2
+1 for the anti-pattern。Stephen Cleary在他的博客文章中也有很好的阐述,如果我没记错的话。基本上他的意思是“让API的使用者自己包装成Tasks..不要自己去做”。 - Simon Whitehead
谢谢您的回答!(请查看我的答案编辑)实际上我并没有创建Task<bool>(抱歉,我认为这是一个错误的复制)。Task<bool>是从调用Task.Factory.FromAsync<bool>()生成的。而且正是这个任务我不确定如何“启动”。有什么想法吗?提前致谢!此外,+1。 - user3175663
@user3175663,请看我对这个问题的评论。 - noseratio - open to work
好的,完美,我会尝试一下。不过,对于async事件处理程序,我认为async void是不好的实践,我错了吗? - user3175663
@user3175663,“async void”事件处理程序可能是该规则最显着的例外,请查看此链接(https://dev59.com/CmIk5IYBdhLWcg3wTcYr)。只需在“async void”事件处理程序内正确处理异常和可重入性即可。请参考此链接(https://dev59.com/KmEh5IYBdhLWcg3wdzaR#22395161)。 - noseratio - open to work

2

在任务执行任何操作之前,您需要启动它。调用 Run 可以实现这一点:

Task<bool> GetTrue()
{
    return Task.Run(() => true);
}

这会输出你期望的结果。 Result 是一个阻塞调用。如果你的Task实例从未启动,Result就永远不会返回。

谢谢您的回答!(请查看我的答案编辑)实际上我并没有创建Task<bool>(抱歉,我认为这是一个错误的复制)。Task<bool>是从调用Task.Factory.FromAsync<bool>()生成的。而且正是这个任务我不确定如何“启动”。有什么想法吗?提前致谢!+1。 - user3175663

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接