Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/276.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C#异步方法仍然挂起UI_C#_Asynchronous_Webclient_Async Ctp_C# 5.0 - Fatal编程技术网

C#异步方法仍然挂起UI

C#异步方法仍然挂起UI,c#,asynchronous,webclient,async-ctp,c#-5.0,C#,Asynchronous,Webclient,Async Ctp,C# 5.0,我有这两种方法,我希望运行异步以保持UI响应。但是,它仍然挂在用户界面上。有什么建议吗 async void DoScrape() { var feed = new Feed(); var results = await feed.GetList(); foreach (var itemObject in results) { var item = new ListViewItem(itemObje

我有这两种方法,我希望运行异步以保持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;
        }
    }
async void DoScrape()
{
var feed=新feed();
var results=await feed.GetList();
foreach(结果中的var itemObject)
{
var item=新的ListViewItem(itemObject.Title);
item.SubItems.Add(itemObject.Link);
item.SubItems.Add(itemObject.Description);
lstfresults.Items.Add(item);
}
}
公共类提要
{
异步公共任务GetList()
{
var client=new WebClient();
string content=wait client.downloadstringtasksync(新Uri(“anyUrl”));
var lstItemObjects=新列表();
var feed=新的XmlDocument();
LoadXml(内容);
var nodes=feed.GetElementsByTagName(“项”);
foreach(节点中的XmlNode节点)
{
var tmpItemObject=new ItemObject();
变量标题=节点[“标题”];
如果(title!=null)tmpItemObject.title=title.InnerText;
变量链接=节点[“链接”];
如果(link!=null)tmpItemObject.link=link.InnerText;
变量说明=节点[“说明”];
如果(description!=null)tmpItemObject.description=description.InnerText;
添加(tmpItemObject);
}
返回对象;
}
}

我怀疑
下载StringTaskAsync
在较低的级别上依赖。在这种情况下,我们知道webrequest的设置不是完全异步的。令人烦恼的是(坦率地说,这是愚蠢的),异步WebRequest的DNS查找阶段是同步执行的,因此会阻塞。我怀疑这可能是你正在观察的问题

以下是文件中的警告:

您有两个选择:

  • 从工作线程启动请求(并且在高负载下,由于阻塞行为,可能会导致线程池不足)
  • (非常)在触发请求之前执行编程DNS查找。这可以异步完成。希望请求将使用缓存的DNS查找

  • 我们选择了第三个(而且代价高昂)选项,实现我们自己的正确异步HTTP库,以获得可观的吞吐量,但在您的情况下,这可能有点极端;)

    您在列表视图中添加了多少项

    除非您采取措施加以阻止,否则每当您将项目添加到列表中时,WinForms列表视图将进行大量处理。这可能需要很长时间,仅添加100个项目可能需要几秒钟

    尝试在循环中使用
    BeginUpdate
    EndUpdate
    将ListView的记账推迟到完成

    async void DoScrape()
    {
        var feed = new Feed();
    
        var results = await feed.GetList();
        LstResults.BeginUpdate();  // Add this
        try
        {
            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);
            }
        }
        finally
        {
            LstResults.EndUpdate();
        }
    }
    

    如果出现异常,必须使用try finally来避免各种痛苦。

    您似乎混淆了异步和并行。它们都基于任务,但它们完全不同。不要假设
    async
    方法是并行运行的——它们不是

    Async默认在同一线程中工作,除非有原因迫使Async引擎启动新线程,例如主线程没有消息泵。但一般来说,我倾向于认为
    async
    关键字运行在同一个线程中

    您使用WinForms,因此UI线程具有消息泵。因此,以上所有代码都在UI线程中运行

    您必须了解,您在这里没有引入任何并行性。您通过
    async
    关键字介绍的是异步操作,不是并行操作。除了对
    DownloadStringTaskAsync
    的一次调用之外,您没有做任何“使您的UI具有响应性”的事情,该调用不会强迫您等待数据到达,但是仍然必须在UI线程中执行所有网络处理(DNS查找等)——下面是正在进行的异步操作(您基本上是“保存”)等待下载的时间)

    为了保持UI的响应性,您需要将耗时的工作分离到单独的线程中,同时保持UI线程空闲。您没有使用
    async
    关键字执行此操作


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

    您如何调用异步方法?我怀疑你打电话给他们,然后以阻塞的方式发布访问结果。你在使用后台线程吗?你在用什么。。。WPF/Silverlight/WinForms?WinForms。没有bg线程。假设项目中唯一的代码和DoScrape()是通过单击按钮调用的。嗯,我看不出这段代码有什么错。解析显然仍然是同步的,但这很可能很便宜。我遵循这本指南,我的方法与这本指南没有太大区别。我只是不明白。但在我看来,下载是他代码中唯一耗时的操作。一点xml解析和UI更新很可能是便宜的@斯宾德的解释听起来更有可能。@CodeInChaos,没错。然而,我只是想说明异步不是并行的。因此,使用“wait”完成的任何操作实际上都是在UI线程中完成的。这包括DNS查找和其他网络访问。这意味着异步代码只保存等待数据到达的时间,而不保存其他任何内容。我将编辑我的答案,使之更具体。这似乎是一个很好的解释。但这对我的情况没有帮助,我将HttpClient.GetByteArrayAsync调用从UI线程上的异步函数移动到了线程池工作线程。