C#异步方法仍然挂起UI

Art*_*t W 9 c# asynchronous webclient c#-5.0 async-ctp

我有这两种方法,我想运行异步以保持UI响应.但是,它仍然悬挂着UI.有什么建议?

async void DoScrape()
    {
        var feed = new Feed();

        var results = await feed.GetList();
        foreach (var itemObject in results)
        {
            var item = new ListViewItem(itemObject.Title);
            item.SubItems.Add(itemObject.Link);
            item.SubItems.Add(itemObject.Description);
            LstResults.Items.Add(item);
        }
    }


    public class Feed
    {
        async public Task<List<ItemObject>> GetList()
        {
            var client = new WebClient();
            string content = await client.DownloadStringTaskAsync(new Uri("anyUrl"));
            var lstItemObjects = new List<ItemObject>();
            var feed = new XmlDocument();
            feed.LoadXml(content);
            var nodes = feed.GetElementsByTagName("item");

            foreach (XmlNode node in nodes)
            {
                var tmpItemObject = new ItemObject();
                var title = node["title"];
                if (title != null) tmpItemObject.Title = title.InnerText;
                var link = node["link"];
                if (link != null) tmpItemObject.Link = link.InnerText;
                var description = node["description"];
                if (description != null) tmpItemObject.Description = description.InnerText;
                lstItemObjects.Add(tmpItemObject);
            }
            return lstItemObjects;
        }
    }
Run Code Online (Sandbox Code Playgroud)

spe*_*der 12

我怀疑DownloadStringTaskAsyncHttpWebRequest.BeginGetResponse在较低的水平上依赖.在这种情况下,已知webrequest的设置不是完全异步的.恼人地(坦率地说,愚蠢地)异步WebRequest的DNS查找阶段是同步执行的,因此会阻塞.我怀疑这可能是你正在观察的问题.

以下转载的是文档中的警告:

在此方法变为异步之前,BeginGetResponse方法需要完成一些同步设置任务(例如,DNS解析,代理检测和TCP套接字连接).因此,永远不应在用户界面(UI)线程上调用此方法,因为它可能需要一些时间,通常为几秒钟.在某些未正确配置webproxy脚本的环境中,这可能需要60秒或更长时间.配置文件元素上的downloadTime属性的默认值是一分钟,这占了潜在时间延迟的大部分.

你有两个选择:

  1. 从工作线程启动请求(并且在高负载下,由于阻塞行为而冒着ThreadPool饥饿的风险)
  2. (Tenuously)在触发请求之前执行编程式DNS查找.这可以异步完成.希望请求将使用缓存的DNS查找.

我们选择了实现我们自己的正确异步HTTP库以获得不错的吞吐量的第三个(也是昂贵的)选项,但在您的情况下它可能有点极端;)


Ste*_*ung 5

你似乎混淆了并行异步.它们都基于任务,但它们完全不同.不要假设async方法并行运行 - 它们不会.

Async默认在同一个线程中工作,除非有理由强制异步引擎启动新线程,例如主线程没有消息泵的情况.但总的来说,我倾向于认为async关键字在同一个线程中运行.

您使用WinForms,因此UI线程有一个消息泵.因此,上面的所有代码都在UI线程中运行.

你必须明白,你没有在这里引入任何并行性.您通过async关键字引入的是异步操作,而不是并行操作.您没有做任何"让您的UI响应",除了那个DownloadStringTaskAsync不会强迫您等待数据到达的调用,但您仍然必须在所有网络处理(DNS查找等) UI线程 - 这是正在进行的异步操作(基本上"节省"等待下载的时间).

为了保持UI的响应性,您需要将耗时的工作分拆到单独的线程中,同时保持UI线程的自由.你不是用async关键字做的.

您需要使用Task.Factory.StartNew(...)显式启动新线程来进行后台处理.