这与.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;
}
答案 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();
}