Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/csharp-4.0/2.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#频繁调用BeginInvoke时UI性能变慢_C# - Fatal编程技术网

C#频繁调用BeginInvoke时UI性能变慢

C#频繁调用BeginInvoke时UI性能变慢,c#,C#,我有一个名为ProxyTesterForm的主窗体,它有一个子窗体ProxyScraperForm。当ProxyScraperForm刮取新代理时,ProxyTesterForm通过异步测试刮取的代理来处理事件,测试后将代理添加到BindingList,BindingList是DataGridView的数据源 因为我正在添加在UI线程上创建的数据绑定列表,所以我在DataGridView上调用BeginInvoke,以便在适当的线程上进行更新 在我将在下面发布的方法中,如果没有BeginInvo

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

因为我正在添加在UI线程上创建的数据绑定列表,所以我在DataGridView上调用BeginInvoke,以便在适当的线程上进行更新

在我将在下面发布的方法中,如果没有BeginInvoke调用,我可以在处理过程中在屏幕上拖动表单,它不会结巴且平滑。对于BeginInvoke呼叫,它的作用正好相反

我对如何解决这个问题有一些想法,但我想听听比我聪明的人的意见,所以我妥善解决了这个问题

  • 使用信号量slim控制同步更新的数量

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

  • 放弃数据绑定的便利,进入虚拟模式

  • 这里还有什么人可以建议的吗

    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); }));
        });
    }
    
  • 注:有关为什么会发生这种情况的实际答案,请参见@Nir的答案。这只是克服som问题并给出一些方向的解释。它并非完美无缺,但它和评论中的对话是一致的

    只需一些快速原型来添加一些层分离(最少的尝试):

    //包含所有实际数据的成员字段
    列表_proxies=新列表();
    //这是一个触发器:它可能是一个计时器或其他东西的ellapsed事件
    OnSomeTimeorOtherTrigger()上的私有void
    { 
    UIupdate();
    }
    //只是一个辅助函数
    私有无效UIupdate
    {
    var local=_proxies.ToList();//确保静态封装
    代理DataGridView.BeginInvoke(新操作(()=>
    {    
    //向UI添加*新内容*的方法
    //对本地副本执行操作
    }));
    }
    私有无效站点\u代理已删除(对象发送方、代理)
    {
    Task.Run(异步()=>
    {
    proxy.IsValid=wait proxy.testvaliditysync(法官[0]);
    //添加到列表中
    _代理。添加(代理);
    });
    }
    
    在Windows中,每个具有UI的线程都有一个消息队列-此队列用于为该线程的Windows发送UI消息,这些消息包括鼠标移动、鼠标上下移动等

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

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

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

    您所做的是用BeginInvoke消息淹没队列,而不是让它处理UI事件

    标准解决方案是限制BeginInvoke呼叫的数量,例如,收集所有需要添加的项目,并使用一个BeginInvoke呼叫来添加所有项目


    或者分批加载,如果您每秒仅对这一秒中找到的所有对象进行一次BeginInvoke调用,则可能不会影响UI响应,用户将无法分辨差异。

    您是否考虑过使用cancelationtoken来阻止多个同时请求?@Stefan否我没有。这不是信号灯的作用吗?我只使用cancellationtokens来取消任务。老实说,我认为您不需要
    任务。请在此处运行
    。改为使
    Site\u ProxyScraped
    异步,因为我假设这是一个事件处理程序。然后等待<代码>代理。TestValueTyYasyc和NeXPixKek可以保持原样。考虑层的分离:它将帮助您克服这里的许多问题。例如,看看这篇(我自己的XD)文章;关键是存储
    proxy.IsValid=wait proxy.testvaliditysync(判断[0])的结果字典
    集合
    中的code>。它将是超快速的,不需要与UI交互。之后,您可以考虑更新UI。也许有一个间隔500毫秒的计时器或者类似的东西。您可以从
    字典
    集合
    更新UI,但我会将
    站点_ProxyScraped
    转换为异步。不需要
    任务。在这里运行
    。@Stefan谢谢,我很快就会尝试一下。如果我使用此解决方案,我会将此标记为答案。@FCin从现在起,我将使用需要等待调用的事件处理程序来执行此操作。@FCin:是的,这是一个很好的建议,尽管我不是调用该函数的对象,并且如果OP能够处理调用方不异步的情况。@davidstanpher:By
    确保静态封装
    ;这是我的俚语:延迟执行和圈地。想象一下,UI正在从
    \u代理列表更新,而该列表在其他地方被更改,这可能是因为UI相对较慢。同时从两个不同的位置访问列表可能会导致问题:即访问可能被删除的元素。因此:制作本地副本可以克服这个问题。这就是那里发生的事情。我添加了一条评论:“在本地副本上执行操作”。感谢您的精彩解释。我觉得情况就是这样,你提出的解决方案和我想的差不多,但我想和比我更了解的人确认一下。
    
    //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);
        });
    }