在多线程中如何调用Winform

每一个从Control类中派生出来的WinForm类(包括Control类)都是依靠底层Windows消息和一个消息泵循环 (message pump loop)来执行的。消息循环都必须有一个相对应的线程,因为发送到一个window的消息实际上只会被发送到创建该 window的线程中去。其结果是,即使提供了同步(synchronization),你也无法从多线程中调用这些处理消息的方法。大多数 plumbing是掩藏起来的,因为WinForm是用代理(delegate)将消息绑定到事件处理方法中的。WinForm将Windows消息转换 为一个基于代理的事件,但你还是必须注意,由于最初消息循环的缘故,只有创建该form的线程才能调用其事件处理方法。如果你在你自己的线程中调用这些方 法,则它们会在该线程中处理事件,而不是在指定的线程中进行处理。你可以从任何线程中调用任何不属于消息处理的方法。
      
Control类(及其派生类)实现了一个定义在System.ComponentModel命名空间下的接口 -- ISynchronizeInvoke ,并以此来处理多线程中调用消息处理方法的问题:

复制   保存
public
 interface
 ISynchronizeInvoke
{
  object
 Invoke(Delegate method,object
[] args);
  IAsyncResult BeginInvoke(Delegate method,object
[] args);
  object
 EndInvoke(IAsyncResult result);
  bool
 InvokeRequired {get
;}
}


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

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

比如,假设你想要从另一个线程中调用某个form中的Close方法,那么你可以使用预先定义好的的MethodInvoke r代理,并调用Invoke 方法:
Form form;

复制   保存
/* obtain a reference to the form,
then: */

ISynchronizeInvoke synchronizer;
synchronizer = form;
if
 (synchronizer.InvokeRequired)
{
MethodInvoker invoker = new

MethodInvoker(form.Close);
synchronizer.Invoke(invoker, null
);
}
else

form.Close();


ISynchronizeInvoke 不仅仅用于WinForm中。例如,一个Calculator类提供了将两个数字相加的Add()方法,它就是通过ISynchronizeInvoke 来实现的。用户必须确定ISynchronizeInvoke .Invoke ()方法的调用是执行在正确的线程中的。 

列表A. Calculator类的Add()方法用于将两个数字相加。如果用户直接调用Add()方法,它会在该用户的线程中执行调用,而用户可以通过ISynchronizeInvoke .Invoke ()将调用写入正确的线程中。

列表A:

复制   保存
public
 class
 Calculator : ISynchronizeInvoke
{
public
 int
 Add(int
 arg1, int
 arg2)
{
int
 threadID = Thread.CurrentThread.GetHashCode();
Trace.WriteLine("Calculator thread ID is "
 + threadID.ToString());
return
 arg1 + arg2;
}
//ISynchronizeInvoke implementation

    public
 object
 Invoke(Delegate method, object
[] args)
{ }
public
 IAsyncResult BeginInvoke(Delegate method, object
[] args)
{ }
public
 object
 EndInvoke(IAsyncResult result)
{ }
public
 bool
 InvokeRequired
{
}
}

 

复制   保存
//Client-side code

public
 delegate
 int
 AddDelegate(int
 arg1,int
 arg2);
int
 threadID = Thread.CurrentThread.GetHashCode();
Trace.WriteLine("Client thread ID is "
 + threadID.ToString());
Calculator calc;
/* Some code to initialize calc */

AddDelegate addDelegate = new
 AddDelegate(calc.Add);
object
[] arr = new
 object
[2];
arr[0] = 3;
arr[1] = 4;
int
 sum = 0;
sum = (int
) calc.Invoke(addDelegate,arr);
Debug.Assert(sum ==7);
/* Possible output:
Calculator thread ID is 29
Client thread ID is 30
*/


或许你并不想进行同步调用,因为它被打包发送到另一个线程中去了。你可以通过BeginInvoke ()和EndInvoke ()方法来实现它。你可以依照通用的.NET非同步编程模式(asynchronous programming model)来使用这些方法:用BeginInvoke ()来发送调用,用EndInvoke ()来实现等待或用于在完成时进行提示以及收集返回结果。 

还值得一提的是ISynchronizeInvoke 方法并非安全类型。 类型不符会导致在执行时被抛出异常,而不是编译错误。所以在使用ISynchronizeInvoke 时要格外注意,因为编辑器无法检查出执行错误。 

实现ISynchronizeInvoke 要求你使用一个代理来在后期绑定(late binding)中动态地调用方法。每一种代理类型均提供DynamicInvoke ()方法: public object DynamicInvoke (object[] 
args); 

理论上来说,你必须将一个方法代理放到一个需要提供对象运行的真实的线程中去,并使Invoke () 和BeginInvoke ()方法中的代理中调用DynamicInvoke ()方法。ISynchronizeInvoke 的实现是一个非同一般的编程技巧,本文附带的源文件中包含了一个名为Synchronizer的帮助类(helper class)和一个测试程序,这个测试程序是用来论证列表A中的Calculator类是如何用Synchronizer类来实现ISynchronizeInvoke 的。Synchronizer是ISynchronizeInvoke 的一个普通实现,你可以使用它的派生类或者将其本身作为一个对象来使用,并将ISynchronizeInvoke 实现指派给它。 

用来实现Synchronizer的一个重要元素是使用一个名为WorkerThread的嵌套类(nested class)。WorkerThread中有一个工作项目(work item)查询。WorkItem类中包含方法代理和参数。Invoke ()和BeginInvoke ()用来将一个工作项目实例加入到查询里。WorkerThread新建一个.NET worker线程,它负责监测工作项目的查询任务。查询到项目之后,worker会读取它们,然后调用DynamicInvoke ()方法。

原文出处:http://www.chenjiliang.com/Article/View.aspx?ArticleID=2288&TypeID=98

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值