您当前的位置: 首页 >  安全

寒冰屋

暂无认证

  • 0浏览

    0关注

    2286博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

使用ISynchronizeInvoke无痛地创建线程安全用户界面

寒冰屋 发布时间:2020-05-17 11:22:41 ,浏览量:0

目录

介绍

概念化这个混乱

编码此混乱

  • 下载源11 KB
介绍

通常,Windows窗体和控件本身不是线程安全的,这会使多线程应用程序中的事情大大复杂化。本文提供了一种线程同步的替代方法,使用编组来执行从主UI线程上的辅助线程调用的一些代码。

概念化这个混乱

线程化很困难,而且很容易出错,当存在同步问题时,它们很容易被追查。如果完全合理,通常我们应该在需要鲁棒代码的情况下尽可能避免多线程。

多线程会使事情复杂化的一种非常常见的情况是更新用户界面,由于Winforms线程不安全,因此通常必须从UI线程完成。

我们将通过将一些代码从调用线程编组到主UI线程,从而使代码本身在UI线程上执行,从而完全避免同步问题。由于所有与UI相关的工作都在UI线程上完成,因此没有同步问题。

输入ISynchronizeInvoke。这个小接口是一个约定,上面写着“嘿,我可以接受委托并在此线程上执行委托”,其中“this thread”是在其ISynchronizeInvoke上创建实现的线程。由于每种形式和每个控件都实现此接口,因此每个控件都可以将委托代码编组到其线程(UI线程)。我们将看到这非常有用。

编码此混乱

一旦掌握了这些代码,它就非常基础。

ISynchronizeInvoke 实现四个重要成员:

  • Invoke() (两个重载)在创建此实例的线程上调用给定委托中的代码
  • BeginInvoke()和EndInvoke()是上述的异步版本——BeginInvoke(),不像Invoke()不会阻塞。
  • InvokeRequired表示我们是否必须使用Invoke()/ BeginInvoke()安全执行。如果为假,则不必整理代理,我们可以直接调用它。这唯一的好处就是性能。不一定必须使用它。

让我们看一下源代码:

// create a thread. we use this instead of tasks
// or thread pooling simply for demonstration.
// normally, it's not a great idea to spawn
// threads directly like this.
var thread = new Thread(() => { 
    // fill the progress bar, slowly
    for(var i = 0;i
        { 
            // execute this code on the UI thread
            Progress.Value = i / 100; 
        };
        // this marshals the code above onto the thread
        // that this form is running on (the UI thread)
        // everything within the action code is executed
        // on the UI thread. We only do this if 
        // InvokeRequired is true, otherwise we can
        // invoke the delegate directly. In this example,
        // it should always be true, but in the real world,
        // it will not be necessarily. Calling Invoke when
        // it's not necessary (InvokeRequired=false) doesn't
        // hurt, but it causes unnecessary overhead.
        if (InvokeRequired)
            Invoke(action);
        else
            action();

        // all controls and forms implement Invoke.
        // There is also BeginInvoke/EndInvoke which 
        // you can use to invoke asynchronously
    }
});
thread.Start();

基本上,我们在这里正在执行的是生成一个线程,并且我们希望该线程在进度条上进行更新。但是,进度条位于UI线程上,因此从另一个线程进行调用并不安全。

为了解决这个问题,而不是实现线程同步,我们可以简单地按上述方式使用Invoke(),这将使用窗体的ISynchronizeInvoke.Invoke()方法在UI线程上执行action中的所有代码。请注意,我们不能直接使用Invoke()或lambda或匿名委托BeginInvoke()。这就是为什么我们把它放在一个名为action的Action委托中。当然,这有点笨拙,但是与必须对UI实现线程安全调用相比,它有了很大的改进。

关注
打赏
1665926880
查看更多评论
立即登录/注册

微信扫码登录

0.0700s