【问题标题】:Async always WaitingForActivation异步总是 WaitingForActivation
【发布时间】:2013-12-29 22:53:14
【问题描述】:

我试图弄清楚 asyncawait 关键字的含义,但是输出不是我所期望的。

控制台应用如下:

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Foo called");
        var result = Foo(5);

        while (result.Status != TaskStatus.RanToCompletion)
        {
            Console.WriteLine("Thread ID: {0}, Status: {1}", Thread.CurrentThread.ManagedThreadId, result.Status);
            Task.Delay(100).Wait();
        }

        Console.WriteLine("Result: {0}", result.Result);
        Console.WriteLine("Finished.");
        Console.ReadKey(true);
    }

    private static async Task<string> Foo(int seconds)
    {
        return await Task.Run(() =>
            {
                for (int i = 0; i < seconds; i++)
                {
                    Console.WriteLine("Thread ID: {0}, second {1}.", Thread.CurrentThread.ManagedThreadId, i);
                    Task.Delay(TimeSpan.FromSeconds(1)).Wait();
                }

                return "Foo Completed.";
            });
    }
}

输出是:

Foo called
Thread ID: 10, Status: WaitingForActivation
Thread ID: 6, second 0.
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 6, second 1.
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 6, second 2.
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 6, second 3.
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 6, second 4.
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Thread ID: 10, Status: WaitingForActivation
Result: Foo Completed.
Finished..

我希望在方法启动后看到状态从WaitingForActivation 发生变化。

它如何保持这种状态并处于活动状态?

【问题讨论】:

  • .FromSeconds(1)).Wait(); 被调用时你期望的状态是什么?
  • 嗨@AlexeiLevenkov 我以为它会更改为RunningWaitingForChildrenToComplete,因为它不再等待。
  • 在您的情况下,这些都没有意义 - 请参阅TaskStatus Enumeration - 任务没有运行,也没有完成自己的代码......我相信唯一有意义的是你看到的那个.
  • @AlexeiLevenkov,因为这个问题看起来很有趣,我已经检查了通过将Foo 中的Task.Delay 替换为长时间运行的循环(计算数百万次迭代的复杂数学),并且状态仍然是只有WaitingForActivation
  • 这是一个非常令人困惑的话题,这个问题和答案并没有为我们未来的互联网搜索者解决任何问题。为什么没有人(NO ONE)能把 async 和 await 解释清楚?从来没有人能够向我解释这一点。

标签: c# .net asynchronous async-await


【解决方案1】:

对于我的回答,值得记住的是 TPL (Task-Parallel-Library)、Task 类和 TaskStatus 枚举是在 async-await 关键字之前引入的,而 async-await 关键字并不是TPL。

在标记为async的方法的上下文中,生成的Task不是表示方法执行的Task,而是表示方法继续执行的Task

这只能利用几种可能的状态:

  • 已取消
  • 故障
  • RanToCompletion
  • 等待激活

我知道Running 似乎是比WaitingForActivation 更好的默认值,但这可能会产生误导,因为在大多数情况下,执行 的异步方法实际上并没有运行(即它可能是await-ing 其他东西)。另一种选择可能是向TaskStatus 添加一个新值,但这可能是对现有应用程序和库的重大更改。

所有这一切都与使用 Task.Run 时非常不同,它是原始 TPL 的一部分,它能够使用 TaskStatus 枚举的所有可能值。

如果您希望跟踪异步方法的状态,请查看IProgress(T) 接口,这将允许您报告正在进行的进度。这篇博文Async in 4.5: Enabling Progress and Cancellation in Async APIs 将提供有关使用IProgress(T) 接口的更多信息。

【讨论】:

  • IProgress 好像只是4.5的接口,有没有办法在4.0的世界里模拟一下?
  • @BanksySan:安装Microsoft.Bcl.Async 包。
  • 如果是关于旧的 TPL,为什么我在使用 TaskFactory.StartNew 时会得到相同的结果? i.imgur.com/nCjtb3s.pngmsdn.microsoft.com/en-us/library/dd321439(v=vs.110).aspx
  • 您的第一个假设是正确的:正在执行的异步方法实际上并未运行(即它可能正在等待其他东西)。实际上,在我的第一个答案中,我想说这样的话。
  • @Selman22 因为你写的是Foo返回的Task的状态,而不是TaskFactory.StartNew返回的任务。
【解决方案2】:

原因是您的result 分配给了返回的Task,它代表您的方法 的延续,并且如果您直接分配Task,则您的方法中有一个不同的Task 正在运行这你会得到你预期的结果:

var task = Task.Run(() =>
        {
            for (int i = 10; i < 432543543; i++)
            {
                // just for a long job
                double d3 = Math.Sqrt((Math.Pow(i, 5) - Math.Pow(i, 2)) / Math.Sin(i * 8));
            }
           return "Foo Completed.";

        });

        while (task.Status != TaskStatus.RanToCompletion)
        {
            Console.WriteLine("Thread ID: {0}, Status: {1}", Thread.CurrentThread.ManagedThreadId,task.Status);

        }

        Console.WriteLine("Result: {0}", task.Result);
        Console.WriteLine("Finished.");
        Console.ReadKey(true);

output

考虑这个以获得更好的解释:你有一个Foo方法,假设它任务A,你有一个Task,假设它任务 B,现在 正在运行 任务,是 任务 B,您的 任务 A 正在等待 任务 B结果。您将结果变量分配给返回的Task,即任务A,因为任务B不返回任务,它返回一个string。考虑一下:

如果你这样定义你的结果:

Task result = Foo(5);

你不会得到任何错误。但是如果你这样定义它:

string result = Foo(5);

你会得到:

无法将类型“System.Threading.Tasks.Task”隐式转换为“字符串”

但是如果你添加一个await关键字:

string result = await Foo(5);

再次,您不会收到任何错误。因为它会等待结果(字符串)并将其分配给您的结果变量。所以最后考虑一下,如果您在 Foo 方法中添加两个任务:

private static async Task<string> Foo(int seconds)
{
    await Task.Run(() =>
        {
            for (int i = 0; i < seconds; i++)
            {
                Console.WriteLine("Thread ID: {0}, second {1}.", Thread.CurrentThread.ManagedThreadId, i);
                Task.Delay(TimeSpan.FromSeconds(1)).Wait();
            }

            // in here don't return anything
        });

   return await Task.Run(() =>
        {
            for (int i = 0; i < seconds; i++)
            {
                Console.WriteLine("Thread ID: {0}, second {1}.", Thread.CurrentThread.ManagedThreadId, i);
                Task.Delay(TimeSpan.FromSeconds(1)).Wait();
            }

            return "Foo Completed.";
        });
}

如果您运行应用程序,您将得到相同的结果。(WaitingForActivation)因为现在,您的任务 A 正在等待这两个任务。

【讨论】:

  • 这个问题看起来很有趣,我已经检查过了。 Foo中没有Task.Delay,我添加了长时间运行循环,状态仍然只有WaitingForActivation
  • 我以为Task.Delay() 创建了一个新任务,所以我等待着。如果我将Task.Delay() 更改为Thread.Sleep(),我会得到相同的输出。
  • 是的,它创建了一个新任务,而您的实际任务正在等待该新任务
  • @Selman22 不会使状态变为WaitingForChildrenToComplete
  • @BanksySan 但是FooAsync还没有完成。
【解决方案3】:

我遇到了同样的问题。答案让我走上了正确的轨道。所以问题是标有 async 的函数没有按预期返回函数本身的任务(而是函数的另一个延续任务)。

所以“await”和“async”关键字搞砸了。最简单的解决方案就是简单地删除它们。然后它按预期工作。如:

static void Main(string[] args)
{
    Console.WriteLine("Foo called");
    var result = Foo(5);

    while (result.Status != TaskStatus.RanToCompletion)
    {
        Console.WriteLine("Thread ID: {0}, Status: {1}", Thread.CurrentThread.ManagedThreadId, result.Status);
        Task.Delay(100).Wait();
    }

    Console.WriteLine("Result: {0}", result.Result);
    Console.WriteLine("Finished.");
    Console.ReadKey(true);
}

private static Task<string> Foo(int seconds)
{
    return Task.Run(() =>
    {
        for (int i = 0; i < seconds; i++)
        {
            Console.WriteLine("Thread ID: {0}, second {1}.", Thread.CurrentThread.ManagedThreadId, i);
            Task.Delay(TimeSpan.FromSeconds(1)).Wait();
        }

        return "Foo Completed.";
    });
}

哪些输出:

Foo called
Thread ID: 1, Status: WaitingToRun
Thread ID: 3, second 0.
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 3, second 1.
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 3, second 2.
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 3, second 3.
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 3, second 4.
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Thread ID: 1, Status: Running
Result: Foo Completed.
Finished.

【讨论】:

    【解决方案4】:

    这段代码似乎为我解决了这个问题。它适用于流媒体课程,因此有一些命名法。

    ''' <summary> Reference to the awaiting task. </summary>
    ''' <value> The awaiting task. </value>
    Protected ReadOnly Property AwaitingTask As Threading.Tasks.Task
    
    ''' <summary> Reference to the Action task; this task status undergoes changes. </summary>
    Protected ReadOnly Property ActionTask As Threading.Tasks.Task
    
    ''' <summary> Reference to the cancellation source. </summary>
    Protected ReadOnly Property TaskCancellationSource As Threading.CancellationTokenSource
    
    ''' <summary> Starts the action task. </summary>
    ''' <param name="taskAction"> The action to stream the entities, which calls
    '''                           <see cref="StreamEvents(Of T)(IEnumerable(Of T), IEnumerable(Of Date), Integer, String)"/>. </param>
    ''' <returns> The awaiting task. </returns>
    Private Async Function AsyncAwaitTask(ByVal taskAction As Action) As Task
        Me._ActionTask = Task.Run(taskAction)
        Await Me.ActionTask '  Task.Run(streamEntitiesAction)
        Try
            Me.ActionTask?.Wait()
            Me.OnStreamTaskEnded(If(Me.ActionTask Is Nothing, TaskStatus.RanToCompletion, Me.ActionTask.Status))
        Catch ex As AggregateException
            Me.OnExceptionOccurred(ex)
        Finally
            Me.TaskCancellationSource.Dispose()
        End Try
    End Function
    
    ''' <summary> Starts Streaming the events. </summary>
    ''' <exception cref="InvalidOperationException"> Thrown when the requested operation is invalid. </exception>
    ''' <param name="bucketKey">            The bucket key. </param>
    ''' <param name="timeout">              The timeout. </param>
    ''' <param name="streamEntitiesAction"> The action to stream the entities, which calls
    '''                                     <see cref="StreamEvents(Of T)(IEnumerable(Of T), IEnumerable(Of Date), Integer, String)"/>. </param>
    Public Overridable Sub StartStreamEvents(ByVal bucketKey As String, ByVal timeout As TimeSpan, ByVal streamEntitiesAction As Action)
        If Me.IsTaskActive Then
            Throw New InvalidOperationException($"Stream task is {Me.ActionTask.Status}")
        Else
            Me._TaskCancellationSource = New Threading.CancellationTokenSource
            Me.TaskCancellationSource.Token.Register(AddressOf Me.StreamTaskCanceled)
            Me.TaskCancellationSource.CancelAfter(timeout)
            ' the action class is created withing the Async/Await function
            Me._AwaitingTask = Me.AsyncAwaitTask(streamEntitiesAction)
        End If
    End Sub
    

    【讨论】:

      【解决方案5】:

      如果有人感兴趣,我会解决这个问题。 在 myMain 方法中,我调用了我的 readasync 方法,例如

      Dispatcher.BeginInvoke(new ThreadStart(() => ReadData()));
      

      我现在一切都很好。

      【讨论】:

        猜你喜欢
        • 2018-04-08
        • 1970-01-01
        • 2019-12-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多