C# 如果线程不';你不能修改那个元素吗?

C# 如果线程不';你不能修改那个元素吗?,c#,winforms,multithreading,C#,Winforms,Multithreading,假设在实例化表单/控件/元素(通常是主线程)的线程中运行的代码不会同时修改/访问该元素,是否可以: 获取文本框的文本属性 枚举列表视图 订阅表单的结束事件。(知道钩子将从实例化该表单的线程调用) 我已经尝试了所有3个,程序似乎没有抱怨它。我一直认为,您必须调用任何想要远程触摸任何与UI相关(读或写)的调用 我非常清楚为什么在修改元素时需要使用IsInvokeRequired/Invoke模式,但我看不出为什么访问属性/事件会导致任何问题。这肯定是可能的,但是,它可能会导致意外行为。此外,还需要考

假设在实例化表单/控件/元素(通常是主线程)的线程中运行的代码不会同时修改/访问该元素,是否可以:

  • 获取文本框的文本属性

  • 枚举列表视图

  • 订阅表单的结束事件。(知道钩子将从实例化该表单的线程调用)

  • 我已经尝试了所有3个,程序似乎没有抱怨它。我一直认为,您必须调用任何想要远程触摸任何与UI相关(读或写)的调用


    我非常清楚为什么在修改元素时需要使用IsInvokeRequired/Invoke模式,但我看不出为什么访问属性/事件会导致任何问题。

    这肯定是可能的,但是,它可能会导致意外行为。此外,还需要考虑其他与线程相关的bug,例如争用条件/死锁,请参阅


    为了安全起见,我会始终坚持在UI线程上访问UI。

    那么,在读取控件时,您如何确保UI线程不会修改控件?封送到UI线程的全部原因是,您不必担心这种情况。特别是枚举列表框将是最容易中断的,因为它将花费最长的时间(这将为竞争条件创建最大的窗口)。您应该为所有这三件事封送到UI线程。

    这是不安全的。当您从另一个线程读取控件时,UI线程可以(也可能会)更改控件的状态。您的读取可能会捕获处于半成品状态的控件。它现在似乎正在发挥作用,但迟早会失败……可能是不可预测和惊人的

    不,您可能不需要使用Invoke模式。坦率地说,这种模式通常是最糟糕的选择。通常最好让工作线程完成繁重的工作,然后通过队列向UI线程发送新数据或进度信息,并让UI线程通过计时器接收这些信息。这有几个优点


    • 消除了昂贵的
      Invoke
      BeginInvoke
      操作
    • UI线程可以决定何时以及多久更新一次新数据,而不是由工作线程来决定
    • 工作线程的吞吐量更高,因为它不必等待
      Invoke
      返回
    • 没有像使用
      BeginInvoke
      那样溢出UI消息队列的风险
    • 它解耦了UI和工作线程的交互

    您需要枚举
    列表视图
    ,并建立一个单独的数据结构,然后工作线程可以安全地访问该结构。如果 ListVIEW 包含很多项目,请考虑与控件保持一个单独的集合。这样,您就可以将处理数据的惩罚分散到一个较长的时间段内,而在这个时间段内,数据可能不会被太多人注意到。毕竟,我们不希望复制操作冻结UI线程,否则用户会注意到。
    ConcurrentBag
    或类似工具可能是一个不错的选择,因为它可以在工作线程读取时由UI线程安全地修改。

    因此,基本上,即使我遵循每个线程最佳实践,仍然可能会有一些未记录的奇怪情况发生?如果不在UI线程上访问UI组件,那么您就没有遵循最佳实践。如果你开始乱搞跨线程访问,它肯定会在某个时候在你脸上爆炸,但不一定马上爆炸。@Deli在一天结束时,重要的是一次只有一个线程访问
    控件。到目前为止,实现这一点最简单的方法是只允许一个线程访问任何控件,因为这样可以保证不会有另一个线程同时修改该控件。如果您可以确保没有其他线程正在修改控件,那么您可以从非UI线程访问它,但由于GUI应用程序中涉及的复杂性,不建议这样做。
    Invoke
    BeginInvoke
    实际上并不那么昂贵。。。事实上,有一个单独的专用计时器可能会更占用资源。第二点是,如果更新太多,可以对更新进行缓冲。对于3,如果这是一个问题,您可以使用非阻塞版本,对于4,同样可以通过缓冲输出来解决,而5是一个特别好的点。不过,我更喜欢用不同的方法来解决这个问题,而不是用定时器。对于以后的版本,您可以使用
    IProgress
    界面。@Servy:您的观点都很好,并且被采纳了。你可以做一些事情来缓和我的观点。最后…当我走
    ISynchronizeInvoke
    路线时,代码总是有味道。