【问题标题】:Using Control.Invoke() in place of lock(Control)使用 Control.Invoke() 代替 lock(Control)
【发布时间】:2011-09-28 17:25:42
【问题描述】:

我正在开发一个多线程应用程序,该应用程序需要在运行时更新 Winforms 控件(A DataGridView)。为了防止对同一个共享资源的多次访问,我从如下锁结构入手:

if (DGV.Columns.Count >= DesiredColumnCount) return;
lock(DGV)
{
    while (DGV.Columns.Count < DesiredColumnCount)
    {
        DGV.Columns.Add(newColumn);
    }
}

然后我意识到,由于 DGV 是在 UI 线程上创建的,因此它必须是 .Invoke()'d。这会将代码更改为:

if (DGV.Columns.Count >= DesiredColumnCount) return;
lock(DGV)
{
    DGV.Invoke(new MethodInvoker(() =>
        {
            while (DGV.Columns.Count < DesiredColumnCount)
            {
                DGV.Columns.Add(newColumn);
            }
        }
}

我的问题是:这不是多余的吗? lock 将阻塞工作线程,直到它拥有对DGV 的独占访问权,Invoke() 将阻塞工作线程,直到 UI 线程可以接收到调用请求并执行代码。我不能只使用Invoke()吗?

(这是主要问题。当然如果上面的代码中还有其他多线程的错误,请评论)

【问题讨论】:

  • 你用什么来做“工作线程”?
  • 你永远不应该阻塞 UI 线程。这适用于锁定以及 Control.Invoke。您应该摆脱锁定并将 Invoke 更改为 BeginInvoke。
  • @PeterRitchie 我不记得到底是什么开始了这个话题。可能是new Thread().Start()System.Threading.Timer.Elapsed。这段代码不会阻塞 UI 线程,它会阻塞工作线程。 BeginInvoke 不能盲目地替换 Invoke,因为它不会阻止调用者:我需要将调用排队,以确保它们不会因以错误的顺序执行而相互绊倒。这可能会通过减少阻塞工作线程所花费的时间来提高性能,但现在这是一个旧项目。

标签: c# .net winforms multithreading locking


【解决方案1】:

有点多余,Invoke 调用会将操作“调度”到 UI 线程。每个后续的Invoke 调用都将以串行方式调度,因此实际上不需要进行任何锁定。

您可以考虑使用BeginInvoke 而不是Invoke 来防止工作线程阻塞,但同样,这将“串行”完成,因此无需担心锁定。

【讨论】:

  • 感谢您的帖子,但我很困惑。 Skeet 先生在 (stackoverflow.com/questions/229554/…) 中说 Control.Invoke 在 UI 线程上执行并等待完成。
  • 是的,他说的是 BeginInvoke()。没关系,无论哪种方式,您都不需要锁。所有代码都在同一个线程上运行,因此无需保护数据。
  • 锁不仅是多余的,而且是等待发生的死锁。如果在调用Invoke 期间调用的某些代码尝试获取锁,那么您将被卡住,因为您的线程正在持有锁,等待 UI 线程上的 Invoke 完成,而 UI 线程正在等待获取锁。
  • @Nathan 抱歉,这是一个措辞不当的解释,我改写了我的答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-04-08
  • 1970-01-01
  • 1970-01-01
  • 2013-02-26
  • 1970-01-01
  • 2010-10-24
  • 2015-09-30
相关资源
最近更新 更多