C#经常调用BeginInvoke时性能缓慢

时间:2018-04-29 07:26:31

标签: c#

我有一个名为ProxyTesterForm的主窗体,它有一个子窗体ProxyScraperForm。当ProxyScraperForm擦除新代理时,ProxyTesterForm通过异步测试被抓取的代理来处理事件,并在测试之后将代理添加到BindingList,这是DataGridView的数据源。

因为我要添加到在UI线程上创建的数据绑定列表,所以我在DataGridView上调用BeginInvoke,因此更新发生在相应的线程上。

如果没有我在下面发布的方法中的BeginInvoke调用,我可以在处理期间在屏幕上拖动表单,它不会断断续续并且很平滑。通过BeginInvoke调用,它正在做相反的事情。

我对如何解决这个问题有一些想法,但是想在这里听到比我更聪明的人的消息,所以我正确地解决了这个问题。

  1. 使用信号量slim来控制同步更新的数量。

  2. 将异步处理的项添加到我将在下面发布的方法范围之外的列表中,并在Timer_Tick事件处理程序中迭代该列表,每隔1秒为列表中的每个项调用BeginInvoke,然后清除该清单并清洗,冲洗,重复直至工作完成。

  3. 放弃数据绑定的便利并进入虚拟模式。

  4. 此处有人可能会提出其他建议。

    private void Site_ProxyScraped(object sender, Proxy proxy)
    {
        Task.Run(async () =>
        {
            proxy.IsValid = await proxy.TestValidityAsync(judges[0]);
            proxiesDataGridView.BeginInvoke(new Action(() => { proxies.Add(proxy); }));
        });
    }
    

2 个答案:

答案 0 :(得分:2)

在Windows中,每个拥有UI的线程都有一个消息队列 - 这个队列用于为该线程的窗口发送UI消息,这些消息包括鼠标移动,鼠标上/下等等。

在每个UI框架中的某个地方都有一个循环,它从队列中读取消息,处理它然后等待下一条消息。

某些消息的优先级较低,例如,只有当线程准备好处理它时才会生成鼠标移动消息(因为鼠标往往会移动很多)

BeginInvoke也使用这种机制,它发送一条消息告诉循环它需要运行的代码。

您正在做的是使用您的BeginInvoke消息充斥队列,而不是让它处理UI事件。

标准解决方案是限制BeginInvoke调用的数量,例如,收集您需要添加的所有项目,并使用一个BeginInvoke调用将它们全部添加。

或者批量添加,如果你对这一秒中发现的所有对象每秒只进行一次BeginInvoke调用,你可能不会影响用户界面的响应能力,而且用户将无法区分它。

答案 1 :(得分:1)

注意:有关为什么发生这种情况的实际答案,请参阅@ Nir的答案。这只是解释som问题并给出一些指示的解释。它并非完美无瑕,但是它通过评论符合对话。

只是一些快速的proto类型来添加一些层的分离(最小的尝试):

//member field which contains all the actual data
List<Proxy> _proxies = new List<Proxy>();

//this is some trigger: it might be an ellapsed event of a timer or something
private void OnSomeTimerOrOtherTrigger()
{ 
      UIupdate();
}

//just a helper function
private void UIupdate
{
    var local = _proxies.ToList(); //ensure static encapsulation 
    proxiesDataGridView.BeginInvoke(new Action(() => 
    {    
         //someway to add *new ones* to UI
         //perform actions on local copy
    }));
}

private void Site_ProxyScraped(object sender, Proxy proxy)
{
    Task.Run(async () =>
    {
        proxy.IsValid = await proxy.TestValidityAsync(judges[0]);
        //add to list
        _proxies.Add(proxy);
    });
}