声明方法返回类型为Task<int>但没有使用async关键字

3
在什么情况下,您会在方法签名中不使用async返回Task<T>
我在以下代码中有这样一个方法,但我很难理解发生了什么。
为什么我的示例代码在任何await语句之后都不执行? 也就是说,为什么Console.WriteLine("4)");Console.WriteLine("3)");return x;永远不会被执行?
class Program
    {
        static void Main(string[] args)
        {
            TestAsync testAsync = new TestAsync();
            testAsync.Run();

            Console.Read();
        }
    }

    public class TestAsync
    {
        public async void Run()
        {
            Task<int> resultTask = GetInt();
            Console.WriteLine("2)");
            int x = await resultTask;
            Console.WriteLine("4)");
        }
        public async Task<int> GetInt()
        {
            Task<int> GetIntAfterLongWaitTask = GetIntAfterLongWait();
            Console.WriteLine("1)");
            int x = await GetIntAfterLongWaitTask;
            Console.WriteLine("3)");
            return x;
        }

        public Task<int> GetIntAfterLongWait()
        {            
            Task.Run(() =>
            {
                for (int i = 0; i < 500000000; i++)
                {
                    if (i % 10000000 == 0)
                    {
                        Console.WriteLine(i);
                    }
                }
            });

            Console.WriteLine("Returning 23");
            return new Task<int>(() => 23);
        }
    }

/*
Output is:
Returning 23
1)
2)
<list of ints>
*/

3
Task<T>是一个合法的类型,可以被任何方法返回。如果该方法前面没有加上async关键字,那么就无法在该方法中使用await - Enigmativity
@Backwards_Dave - 编译器没有警告或错误,因为这是有效的代码。 - Enigmativity
2
Task 构造函数实际上没有任何用例。 - Stephen Cleary
1
只能在标记为async的方法内部使用await关键字。这与你调用的方法的标记无关。 - Enigmativity
1
@Backwards_Dave - 请阅读一下https://blogs.msdn.microsoft.com/pfxteam/2011/01/13/await-anything/。 - Enigmativity
显示剩余4条评论
3个回答

6
当你从一个非异步方法返回一个任务(Task)时,
Task通常被称为对未来值的承诺。无论你是从一个带有async关键字的方法中产生这个承诺,还是从其他来源(比如启动的线程或IO回调)产生的,都不会影响调用者,只是一种实现策略。这也是为什么接口(或抽象方法定义)根本没有async关键字的原因(async/await是一种实现策略)。
你的代码示例:
问题1:你的方法GetIntAfterLongWait有两个问题。首先,第一个实例化的Task被分配到一个线程上,但结果从未被获取(因此从未等待...也从未延迟任何操作)。
问题2:第二个任务(在GetIntAfterLongWait中返回的任务)是通过构造函数创建的(带有要执行的方法),但没有启动(Task.Start())。其他更简单的方法包括静态的Task.Run或(在这种情况下)Task.FromResult。该任务(或承诺)从未提供结果,因为构造函数中的函数块从未执行。
问题3:Main方法没有等待返回任务的结果(无论是通过await、Task.Wait()还是Task.Result)。

2
你的代码问题在于你实际上从未开始等待任务,因为方法GetIntAfterLongWait返回一个新的、未启动的任务实例。所以基本上你陷入了死锁,等待着根本没有开始的东西。
你可以返回Task.FromResult(23),这基本上是已经完成的任务,或者你可以运行你的任务Task.Run(() => 23);

2
不建议使用 Task.Factory.StartNew(https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.taskfactory.startnew?view=netframework-4.7.2#remarks)除非在特殊情况下,而此情况不属于其中之一。 - ckuri

0

你可能正在GetIntAfterLongWait()方法中创建一个新的线程。尝试将return new Task<int>(() => 23);更改为return Task.FromResult(23);

有关Task.FromResult的更多信息,请参阅如何:创建预计算任务上的MSDN文档(附有良好的示例)。


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