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

目录

介绍

概念化这个混乱

编码此混乱


介绍

通常,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<10001;++i)
    {
        // update the progress bar in a thread safe manner
        // you can't use a lambda here without assigning
        // it to a specific delegate type. Here, we use
        // Action since it doesn't need any arguments
        // or a return value
        Action action = () =>
        { 
            // 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()。这就是为什么我们把它放在一个名为actionAction委托中。当然,这有点笨拙,但是与必须对UI实现线程安全调用相比,它有了很大的改进。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值