【问题标题】:Status of Task getting stuck at "WaitingForActivation"任务状态卡在“WaitingForActivation”
【发布时间】:2014-03-04 07:19:46
【问题描述】:

这是关于 .NET 4.0

我正在尝试使用 Task 从多个 WebAPI 异步获取数据。在控制台应用程序中,程序运行良好,但是当我创建 Web 应用程序时,任务的状态总是卡在“WaitingForActivation”。任何帮助将不胜感激。以下是我的一段代码:

    public String[] CallClientAPI(string[] addresses)
    {
        CancellationTokenSource cts = new CancellationTokenSource();
        int timeToWait = 3000; //wait for 3 seconds to fetch API Data

        Task.Factory.StartNew(() =>
        {
            Thread.Sleep(timeToWait);
            cts.Cancel();
        });

        Task<String[]> webTask = GetAPIData(addresses, cts.Token);
        //webTask is always in "WaitingForActivation" mode only ..

        if (!webTask.IsCompleted)
        {
            Thread.SpinWait(5000000);
        }

        String[] results = null;

        try
        {
            results = webTask.Result;
        }
        catch
        {               
        }
        //other code
    }

编辑:

--------GetAPIData 的代码--------

    public Task<string[]> GetAPIData(String[] urls, CancellationToken token)
    {
        TaskCompletionSource<string[]> tcs = new TaskCompletionSource<string[]>();
        WebClient[] webClients = new WebClient[urls.Length];

        token.Register(() =>
            {
                foreach (var wc in webClients)
                {
                    if (wc != null)
                        wc.CancelAsync();
                }                    
            });

        object m_lock = new object();
        int count = 0;
        List<string> results = new List<string>();
        for (int i = 0; i < urls.Length; i++)
        {
            webClients[i] = new WebClient();

            webClients[i].DownloadStringCompleted += (obj, args) =>
                {
                    if (args.Cancelled == true)
                    {
                        tcs.TrySetCanceled();
                        return;
                    }
                    else if (args.Error != null)
                    {
                        tcs.TrySetException(args.Error);
                        return;
                    }
                    else
                    {
                        results.Add(args.Result);
                    }

                    lock (m_lock)
                    {
                        count++;
                        if (count == urls.Length)
                        {
                            tcs.TrySetResult(results.ToArray());
                        }
                    }
                };

            Uri address = null;
            try
            {
                address = new Uri(urls[i]);
                webClients[i].DownloadStringAsync(address, address);
            }

            catch (UriFormatException ex)
            {
                tcs.TrySetException(ex);
                return tcs.Task;
            }
        }

        return tcs.Task;
    }

【问题讨论】:

  • 天啊Thread.SpinWait(5000000); ?你确定你在做什么?
  • 这只是一个复制粘贴..我也尝试将这一行注释掉。但是同样的“WaitingForActivation”占了上风:(
  • GetAPIData的代码?
  • 对我来说看起来不错,当DownloadStringCompleted 触发并完成所有任务时,任务状态最终应该会改变。
  • 但它没有......也许我可能遗漏了一些东西......不过这在控制台应用程序中工作正常。

标签: .net c#-4.0 asynchronous task-parallel-library


【解决方案1】:

您似乎正在 UI 线程上开始下载。问题在于 Completed 处理程序正试图在 UI 线程上执行。但这是不可能的,因为 UI 线程在调用 Result 时被阻塞。结果就是死锁了。

解决这个问题的正确方法是不要阻塞 UI 线程。这在 C# 5.0 中特别有效(假设您使用的是 VS 2012,您可以与 .Net 4.0 一起使用),但在 C# 4.0 中也可以使用 ContinueWith() 之类的东西。

如果您真的想在处理下载时阻塞 UI 线程(我真的不建议这样做),那么在单独的线程上执行所有 WebClient 代码(使用 Task.Factory.StartNew())。这样,Completed 处理程序就不会尝试在 UI 线程上恢复,从而解决死锁。

【讨论】:

  • 谢谢!经过进一步研究,我在这里发现了同样的问题:blog.stephencleary.com/2012/07/dont-block-on-async-code.html 为了解决这个问题,我使用了一个带有取消令牌的 Parallel.Foreach 循环,在 action 方法中,我正常触发了请求。就像变魔术一样!
【解决方案2】:

这是按预期工作的。简单明了!!

    public String[] CallClientAPI(string[] addresses)
    {
        int timeToWait = Int32.Parse(ConfigurationManager.AppSettings["API_QUERY_TIMEOUT"].ToString());
        CancellationTokenSource cts = new CancellationTokenSource();

        Task.Factory.StartNew(() =>
            {
                Thread.Sleep(timeToWait);
                cts.Cancel();
            });

        ParallelOptions po = new ParallelOptions();
        po.CancellationToken = cts.Token;

        try
        {
            Parallel.ForEach(addresses, po, (address) =>
                {
                    try
                    {
                        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(address);
                        request.Method = "GET";
                        request.ContentType = "text/xml";
                        request.Timeout = timeToWait;

                        WebResponse response = request.GetResponse();

                        Stream respstream = response.GetResponseStream();
                        Encoding encode = System.Text.Encoding.GetEncoding("utf-8");
                        StreamReader sr = new StreamReader(respstream, encode);

                        myresult.Add(sr.ReadToEnd());
                    }
                    catch
                    {
                    }
                });
        }
        catch
        {
        }

        return myresult.ToArray();
    }

【讨论】:

    猜你喜欢
    • 2013-06-28
    • 2012-03-04
    • 1970-01-01
    • 2022-08-23
    • 2020-08-04
    • 2019-05-13
    • 2022-11-04
    • 2018-04-08
    • 1970-01-01
    相关资源
    最近更新 更多