Task.WaitAll不等待其他异步方法 [英] Task.WaitAll not waiting on other async methods
问题描述
我正在使用Microsoft.Bcl库(不带Task.WhenAll)的可移植类库异步检索一些rss文章.每篇文章都有一个rss注释的URL,我也需要异步检索该注释.
I'm asynchronously retrieving some rss articles with my Portable Class Library that uses the Microsoft.Bcl library (which doesn't have Task.WhenAll). Each article has a url to rss comments that I need to asynchronously retrieve as well.
下面的代码是我的库.我调用GetArticles(),但是它不返回任何创建调用GetComments()以异步获取评论的任务列表的任何内容.
The code below is my library. I call GetArticles() but it does not return any of the which creates a list of tasks that call GetComments() to asynchronously get the comments.
我尝试在GetArticles中使用Task.WaitAll等待注释,但它不会阻塞线程.任何帮助将不胜感激.
I've tried using Task.WaitAll in GetArticles to wait for the comments but it does not block the thread. Any help would be appreciated.
private const string ArticlesUri = "";
public async Task<List<ArticleBrief>> GetArticles()
{
var results = new List<ArticleBrief>();
try
{
var wfw = XNamespace.Get("http://wellformedweb.org/CommentAPI/");
var media = XNamespace.Get("http://search.yahoo.com/mrss/");
var dc = XNamespace.Get("http://purl.org/dc/elements/1.1/");
var t = await WebHttpRequestAsync(ArticlesUri);
StringReader stringReader = new StringReader(t);
using (var xmlReader = System.Xml.XmlReader.Create(stringReader))
{
var doc = System.Xml.Linq.XDocument.Load(xmlReader);
results = (from e in doc.Element("rss").Element("channel").Elements("item")
select
new ArticleBrief()
{
Title = e.Element("title").Value,
Description = e.Element("description").Value,
Published = Convert.ToDateTime(e.Element("pubDate").Value),
Url = e.Element("link").Value,
CommentUri = e.Element(wfw + "commentRss").Value,
ThumbnailUri = e.Element(media + "thumbnail").FirstAttribute.Value,
Categories = GetCategoryElements(e.Elements("category")),
Creator = e.Element(dc + "creator").Value
}).ToList();
}
var tasks = new Queue<Task>();
foreach (var result in results)
{
tasks.Enqueue(
Task.Factory.StartNew(async ()=>
{
result.Comments = await GetComments(result.CommentUri);
}
));
}
Task.WaitAll(tasks.ToArray());
}
catch (Exception ex)
{
// should do some other
// logging here. for now pass off
// exception to callback on UI
throw ex;
}
return results;
}
public async Task<List<Comment>> GetComments(string uri)
{
var results = new List<Comment>();
try
{
var wfw = XNamespace.Get("http://wellformedweb.org/CommentAPI/");
var media = XNamespace.Get("http://search.yahoo.com/mrss/");
var dc = XNamespace.Get("http://purl.org/dc/elements/1.1/");
var t = await WebHttpRequestAsync(uri);
StringReader stringReader = new StringReader(t);
using (var xmlReader = System.Xml.XmlReader.Create(stringReader))
{
var doc = System.Xml.Linq.XDocument.Load(xmlReader);
results = (from e in doc.Element("rss").Element("channel").Elements("item")
select
new Comment()
{
Description = e.Element("description").Value,
Published = Convert.ToDateTime(e.Element("pubDate").Value),
Url = e.Element("link").Value,
Creator = e.Element(dc + "creator").Value
}).ToList();
}
}
catch (Exception ex)
{
// should do some other
// logging here. for now pass off
// exception to callback on UI
throw ex;
}
return results;
}
private static async Task<string> WebHttpRequestAsync(string url)
{
//TODO: look into getting
var request = WebRequest.Create(url);
request.Method = "GET";
var response = await request.GetResponseAsync();
return ReadStreamFromResponse(response);
}
private static string ReadStreamFromResponse(WebResponse response)
{
using (Stream responseStream = response.GetResponseStream())
using (StreamReader sr = new StreamReader(responseStream))
{
string strContent = sr.ReadToEnd();
return strContent;
}
}
private List<string> GetCategoryElements(IEnumerable<XElement> categories)
{
var listOfCategories = new List<string>();
foreach (var category in categories)
{
listOfCategories.Add(category.Value);
}
return listOfCategories;
}
解决方案中的更新代码,只是在Enqueue方法上添加了.UnWrap():
Updated Code from Solution, just added .UnWrap() on the Enqueue method:
var tasks = new Queue<Task>();
foreach (var result in results)
{
tasks.Enqueue(
Task.Factory.StartNew(async ()=>
{
result.Comments = await GetComments(result.CommentUri);
}
).Unwrap());
}
Task.WaitAll(tasks.ToArray());
推荐答案
正在适当等待.问题是您正在创建一个Task
,该Task
创建了另一个任务(即StartNew
返回一个Task<Task>
,而您只在等待外部的Task
,它很快就完成了(它在内部Task完成之前就已经完成了) )).
It is waiting appropriately. The problem is that you are creating a Task
which creates another task (i.e. StartNew
is returning a Task<Task>
and you are only waiting on the outer Task
which completes rather quickly (it completes before the inner Task is complete)).
问题将是:
- 您真的想要这个内在的任务吗?
- 如果是,则可以使用
Task.Unwrap
获取代表内部Task
和外部Task
均已完成的代理任务,并使用它来等待. - 如果否,那么您可以在StartNew中删除
async
/await
的使用,以便不存在内部任务(我认为这是首选,尚不清楚为什么需要内部任务). /li>
- Do you really want that inner task?
- If yes, then you can use
Task.Unwrap
to get a proxy task that represents the completion of both the inner and outerTask
and use that to Wait on. - If no, then you could remove the use of
async
/await
in StartNew so that there is not an inner task (I think this would be prefered, it's not clear why you need the inner task).
顺便说一句,如果您不使用C#5,请注意关闭foreach变量
result
请参见As an aside, if you are not using C# 5, then watch out for closing over the foreach variable
result
See- Has foreach's use of variables been changed in C# 5?, and
- http://blogs.msdn.com/b/ericlippert/archive/2009/11/12/closing-over-the-loop-variable-considered-harmful.aspx)
这篇关于Task.WaitAll不等待其他异步方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!
- If yes, then you can use
- 如果是,则可以使用