【问题标题】:Task.WaitAll not waiting on other async methodsTask.WaitAll 不等待其他异步方法
【发布时间】:2013-05-21 16:45:48
【问题描述】:

我正在使用我的可移植类库异步检索一些 rss 文章,该类库使用 Microsoft.Bcl 库(它没有 Task.WhenAll)。每篇文章都有一个我需要异步检索的 rss cmets 的 url。

下面的代码是我的库。我调用 GetArticles() 但它不返回任何创建调用 GetComments() 以异步获取 cmets 的任务列表。

我尝试在 GetArticles 中使用 Task.WaitAll 来等待 cmets,但它不会阻塞线程。任何帮助将不胜感激。

    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():

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

【问题讨论】:

标签: c# .net asynchronous async-await portable-class-library


【解决方案1】:

它正在适当地等待。问题是您正在创建一个 Task 来创建另一个任务(即 StartNew 正在返回一个 Task&lt;Task&gt; 并且您只等待外部 Task 完成相当快(它在内部任务完成之前完成))。

问题将是:

  • 你真的想要那个内心的任务吗?
    • 如果是,那么您可以使用Task.Unwrap 获取代表内部和外部Task 完成的代理任务并使用它来等待。
    • 如果不是,那么您可以在 StartNew 中删除async/await 的使用,这样就没有内部任务(我认为这是首选,不清楚为什么需要内部任务)。
  • 您真的需要对异步任务执行同步Wait 吗?阅读 Stephen Cleary 的一些博客:http://blog.stephencleary.com/2012/02/async-unit-tests-part-1-wrong-way.html

顺便说一句,如果您不使用 C# 5,请注意关闭 foreach 变量 result 请参阅

【讨论】:

  • Do you really need to do a synchronous Wait on an asynchronous Task? 好吧,如果他不这样做,他应该如何更改它,因为他不能使用WhenAll?提示:您可以使用ContinueWhenAll 编写自己的WhenAll 方法。
【解决方案2】:

在Microsoft.Bcl.Async 中,我们无法向Task 添加任何静态方法。不过TaskEx上的大部分方法都可以找到,比如TaskEx.WhenAll()确实存在。

【讨论】:

  • 谢谢!这就是我正在寻找的,用于 WP7 的 WhenAll()。 :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-26
  • 1970-01-01
  • 2018-12-19
  • 1970-01-01
  • 2016-09-12
  • 1970-01-01
相关资源
最近更新 更多