任务陷入" WaitingForActivation"

时间:2014-03-04 07:19:46

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

这与.NET 4.0有关

我正在尝试使用Task异步从多个WebAPI中获取数据。在控制台应用程序中,该程序工作得很好,但是当我创建一个Web应用程序时,Task的状态总是停留在“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;
    }

2 个答案:

答案 0 :(得分:1)

看起来你正在UI线程上开始下载。问题在于Completed处理程序正在尝试在UI线程上执行。但这是不可能的,因为在调用Result时UI线程被阻止了。结果就是你遇到了僵局。

对此的正确解决方案是不阻止UI线程。这在C#5.0中很有效(你可以使用.Net 4.0,假设你使用的是VS 2012),但在C#4.0中也可以使用像ContinueWith()这样的东西。

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

答案 1 :(得分:0)

这正是所希望的。简单明了!!

    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();
    }