• 线程间的操作(转)


     一个简单的Form, 按钮btnTest是enabled=false。在btnEnable的Click事件中 创建线程,在线程中尝试设置btnTest.Enabled = true; 发生异常:线程间操作无效: 从不是创建控件“btnTest”的线程访问它。

    代码如下:

    复制代码
     1 using System;
    2 using System.Threading;
    3 using System.Windows.Forms;
    4
    5 namespace TestingUIThread
    6 {
    7 publicpartialclass Form1 : Form
    8 {
    9 Thread thread =null;
    10
    11 public Form1()
    12 {
    13 InitializeComponent();
    14 }
    15
    16 privatevoid btnEnable_Click(object sender, EventArgs e)
    17 {
    18 thread =new Thread(new ParameterizedThreadStart(EnableButton));
    19 thread.Start(null);
    20 }
    21
    22 privatevoid EnableButton(object o)
    23 {
    24 // following line cause exception, details as below:
    25 //Cross-thread operation not valid: Control 'btnTest' accessed from a thread other than the thread it was created on.
    26 btnTest.Enabled =true;
    27 btnTest.Text ="Enabled";
    28 }
    29 }
    30 }
    复制代码

      在默认情况下,C#不准许在一个线程中直接访问或操作另一线程中创建的控件,这是因为访问windows窗体控件本质上是不安全的。

      线程之间是可以同时运行的,那么如果有两个或多个线程同时操作某一控件的某状态,尝试将一个控件变为自己需要的状态时, 线程的死锁就可能发生。

      为了区别是否是创建该控件的线程访问该控件,Windows窗体控件中的每个控件都有一个InvokeRequired属性,这个属性就是用来检查本控件是否被其他线程调用的属性,当被创建该线程外的线程调用的时候InvokeRequired就为true。有了这个属性我们就可以利用它来做判断了。

      光判断出是否被其他线程调用是没有用的,所以windows窗体控件中还有一个Invoke方法可以帮我们完成其他线程对控件的调用。结合代理的使用就可以很好的完成我们的目标。

    上面问题的解决方法:

    方法一:

      public Form1()
      {            
                InitializeComponent();
        CheckForIllegalCrossThreadCalls = false;
      }

      //
      // Summary:
      // Gets or sets a value indicating whether to catch calls on the wrong thread
      // that access a control's System.Windows.Forms.Control.Handle property when
      // an application is being debugged.
      //
      // Returns:
      // true if calls on the wrong thread are caught; otherwise, false.
      [EditorBrowsable(EditorBrowsableState.Advanced)]
      [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
      [SRDescription("ControlCheckForIllegalCrossThreadCalls")]
      [Browsable(false)]
      public static bool CheckForIllegalCrossThreadCalls { get; set; }

    方法二:

    复制代码
     1 using System;
    2 using System.Threading;
    3 using System.Windows.Forms;
    4
    5 namespace TestingUIThread
    6 {
    7 publicpartialclass Form1 : Form
    8 {
    9 Thread thread =null;
    10
    11 public Form1()
    12 {
    13
    14 InitializeComponent();
    15 }
    16
    17 privatevoid btnEnable_Click(object sender, EventArgs e)
    18 {
    19 thread =new Thread(new ParameterizedThreadStart(EnableButton));
    20 thread.Start(null);
    21 }
    22
    23 privatevoid EnableButton(object o)
    24 {
    25 EnableButton();
    26 }
    27
    28 privatedelegatevoid EnableButtonCallBack();
    29
    30 privatevoid EnableButton()
    31 {
    32 if (this.btnTest.InvokeRequired)
    33 {
    34 this.Invoke(new EnableButtonCallBack(EnableButton));
    35 }
    36 else
    37 {
    38 btnTest.Enabled =true;
    39 btnTest.Text ="Enabled";
    40 }
    41 }
    42 }
    43 }
    复制代码

    方法三: 通过ISynchronizeInvoke和MethodInvoker.

    复制代码
     1 using System;
    2 using System.ComponentModel;
    3 using System.Threading;
    4 using System.Windows.Forms;
    5
    6 namespace TestingUIThread
    7 {
    8 publicpartialclass Form1 : Form
    9 {
    10 Thread thread =null;
    11
    12 public Form1()
    13 {
    14 InitializeComponent();
    15 }
    16
    17 privatevoid btnEnable_Click(object sender, EventArgs e)
    18 {
    19 thread =new Thread(new ParameterizedThreadStart(MyEnableButton));
    20 thread.Start(null);
    21 }
    22
    23 privatevoid MyEnableButton(object sender)
    24 {
    25 ISynchronizeInvoke synchronizer =this;
    26
    27 if (synchronizer.InvokeRequired)
    28 {
    29 MethodInvoker minvoker =new MethodInvoker(this.EnableButton);
    30 synchronizer.Invoke(minvoker, null);
    31 }
    32 else
    33 {
    34 this.EnableButton();
    35 }
    36 }
    37
    38 privatevoid EnableButton()
    39 {
    40 btnTest.Enabled =true;
    41 btnTest.Text ="Enabled";
    42 }
    43 }
    44 }
    复制代码

    MyEnableButton方法也可以如下:

    复制代码
    privatevoid MyEnableButton(object sender)
    {
    if (this.InvokeRequired)
    {
    MethodInvoker minvoker
    =new MethodInvoker(this.EnableButton);
    this.Invoke(minvoker, null);
    }
    else
    {
    this.EnableButton();
    }
    }
    复制代码

    对于方法三,可用于form中调用其他form的方法,具体解释及用法如下:

      每一个从Control类中派生出来的WinForm类(包括Control类)都是依靠底层Windows消息和一个消息泵循环(message pump loop)来执行的。消息循环必须有一个对应的线程,因为发送window的消息实际上消息只会被发送到创建该window的线程中去。其结果是,即使提供了同步(synchronization),也无法从多线程中调用这些处理消息的方法。
    大多数plumbing是掩藏起来的,因为WinForm是用代理(delegate)将消息绑定到事件处理方法中的。WinForm将Windows消息转换为一个基于代理的事件,但是必须注意,由于最初消息循环的缘故,只有创建该form的线程才能调用其事件处理方法。如果其他线程中调用这些方法,则它们会在该线程中处理事件,而不是在指定的线程中进行处理。

      Control类(及其派生类)实现了一个定义在System.ComponentModel命名空间下的接口(ISynchronizeInvoke),并以此来处理多线程中调用消息处理方法的问题:

    复制代码
    publicinterface ISynchronizeInvoke
    {
     
    object Invoke(Delegate method,object[] args);
     IAsyncResult BeginInvoke(Delegate method,
    object[] args);
     
    object EndInvoke(IAsyncResult result);
     
    bool InvokeRequired {get;}
    }
    复制代码

    Invoke  同步调用

    BeginInvoke和EndInvoke  异步调用,用BeginInvoke()来发送调用,用EndInvoke()来实现等待或用于在完成时进行提示以及收集返回结果。

      ISynchronizeInvoke提供了一个普通的标准机制用于在其他线程的对象中进行方法调用。
      例如,如果一个对象实现了ISynchronizeInvoke,那么在线程T1上的客户端可以在该对象中调用ISynchronizeInvoke的Invoke()方法。Invoke()方法的实现会阻塞(block)该线程的调用,它将调用打包发送(marshal)到 T2,并在T2中执行调用,再将返回值发送会T1,然后返回到T1的客户端。Invoke()方法以一个代理来定位该方法在T2中的调用,并以一个普通的对象数组做为其参数。

      调用者可以检查InvokeRequired属性,因为既可以在同一线程中调用ISynchronizeInvoke也可以将它重新定位(redirect)到其他线程中去。如果InvokeRequired的返回值是false的话,则调用者可以直接调用该对象的方法。

      比如,要从另一个线程中调用某个form中的method方法,那么可以使用预先定义好的的MethodInvoker代理,并调用Invoke方法:

    复制代码
    ISynchronizeInvoke synchronizer = form;

    if(synchronizer.InvokeRequired)
    {
    MethodInvoker invoker
    =new MethodInvoker(form.method);
    synchronizer.Invoke(invoker,
    null);
    }
    else
    {
    form.method();
    }
  • 相关阅读:
    Flask基础教程
    htmlrunner-unittest生成多样的测试报告
    requestz-基于pycurl封装的requests库
    emailz-使发送邮件更方便
    filez-方便的数据文件加载器
    logz-易用的日志记录器
    基本语言(二)
    基本语言(一)
    编程范式(Programming paradigm)
    deque 双向队列知识点汇总
  • 原文地址:https://www.cnblogs.com/hanjiaxu/p/3295376.html
Copyright © 2020-2023  润新知