1、线程之间的通信,Private int Add(int a,int b)return a+b;/创建局长1 Private int Minus(int a, int b)return a-b;/创建局长2 Private delegate int AddDelegate(int a,int b)/创建秘书 AddDelegate ad=new AddDelegate(Add); /创建局长1和秘书的关系 ad+=Minus;/创建局长2和秘书的关系/*Int reuslt=ad(3,4);/让秘书帮我办这个事情,本质上会调用Add(3,4)方法得到结果7*/ad(4,3)/本质上同时调用Add
2、(4,3)和Minus(4,3)方法,就可以得到结果7和1 Private Event AddDelegate AddDelegateEvent;/创建夫人 AddDelegateEvent += new AddDelegate(Add); /创建局长和夫人的关系 Int reusult =AddDelegateEvent(3,4); /让局长夫人帮我办这个事情 本质上会调用Add(3,4)方法得到结果7 Php jsp aspx asp Java 开发jsp 同flash 代码版 flex ActionScript3.0 微软开发C#对抗Java在web方面 .java .class asp
3、x .dll 微软开发Silivlight 对抗Adobe公司的flash动画,AddDelegateEvent事件触发,Static readOnly 与const区别 : Static readOnly :字段值是在运行时确定 Const :字段值是在编译之前确定 Class Add Const PI =3.14; Static readOnly OPI;Add(int n)this.OPI =n; /This.OPI =3.149;, 模型线程之间交流 Window 线程之间通信 通过API sendMessage()/postMessage() 消息列表queue .net 1 con
4、trol.ivoke /beginivoke .net 2 delegate.ivoke/beginivoke .net 3 synchronizationContexAsynOperation.send/post 在winform中一般主线统称为UI线程 该UI线程负责处理所以控件实例化及渲染图形职责同时负责更新这样控件值 封送代码 Thread类 ThreadPool类 构造函数:在new实例化对象是首先执行的方法 析构函数:在该对象作为垃圾回收最后执行的方法 该两个方法一般定义都是对立的,如创建一个数据库连接类 常常在构造函数创建数据库连接 在析构函数创建数据库连接关闭,浅谈Contro
5、l类提供了Invoke和BeginInvoke机制,windows程序消息机制,Windows程序有个消息队列,窗体上的所有消息是这个队列里面消息的最主要来源。这里的while循环使用了GetMessage()这个方法,这是个阻塞方法,也就是队列为空时方法就会被阻塞,从而这个while循环停止运动,这避免了一个程序把cpu无缘无故地耗尽,让其它程序难以得到响应。当然在某些需要cpu最大限度运动的程序里面就可以使用另外的方法,例如某些3d游戏或者及时战略游戏中,一般会使用PeekMessage()这个方法,它不会被windows阻塞,从而保证整个游戏的流畅和比较高的帧速。这个主线程维护着整个窗体
6、以及上面的子控件。当它得到一个消息,就会调用DispatchMessage方法派遣消息,这会引起对窗体上的窗口过程的调用。窗口过程里面当然是程序员提供的窗体数据更新代码和其它代码,dotnet里面的消息循环,public static void Main(string args)Form f = new Form();Application.Run(f);Dotnet窗体程序封装了上述的while循环,这个循环就是通过Application.Run方法启动的。,线程外操作GUI控件的问题,如果从另外一个线程操作windows窗体上的控件,就会和主线程产生竞争,造成不可预料的结果,甚至死锁。因此
7、windows GUI编程有一个规则,就是只能通过创建控件的线程来操作控件的数据,否则就可能产生不可预料的结果。 因此,dotnet里面,为了方便地解决这些问题,Control类实现了ISynchronizeInvoke接口,提供了Invoke和BeginInvoke方法来提供让其它线程更新GUI界面控件的机制。,public interface ISynchronizeInvokeHostProtection(SecurityAction.LinkDemand, Synchronization=true, ExternalThreading=true)IAsyncResult BeginIn
8、voke(Delegate method, object args);object EndInvoke(IAsyncResult result);object Invoke(Delegate method, object args);bool InvokeRequired get; 如果从线程外操作windows窗体控件,那么就需要使用Invoke或者BeginInvoke方法,通过一个委托把调用封送到控件所属的线程上执行。,消息机制-线程间和进程间通信机制,Windows消息机制是windows平台上的线程或者进程间通信机制之一。Windows消息值其实就是定义的一个数据结构,最重要的是消息
9、的类型,它就是一个整数;然后就是消息的参数。消息的参数可以表示很多东西。Windows提供了一些api用来向一个线程的消息队列发送消息。因此,一个线程可以向另一个线程的消息队列发送消息从而告诉对方做什么,这样就完成了线程间的通信。有些api发送消息需要一个窗口句柄,这种函数可以把消息发送到指定窗口的主线程消息队列;而有些则可以直接通过线程句柄,把消息发送到该线程消息队列中。,SendMessage是windows api,用来把一个消息发送到一个窗口的消息队列。这个方法是个阻塞方法,也就是操作系统会确保消息的确发送到目的消息队列,并且该消息被处理完毕以后,该函数才返回。返回之前,调用者将会被暂
10、时阻塞。PostMessage也是一个用来发送消息到窗口消息队列的api函数,但这个方法是非阻塞的。也就是它会马上返回,而不管消息是否真的发送到目的地,也就是调用者不会被阻塞。,Invoke and BeginInvoke,Invoke或者BeginInvoke方法都需要一个委托对象作为参数。委托类似于回调函数的地址,因此调用者通过这两个方法就可以把需要调用的函数地址封送给界面线程。这些方法里面如果包含了更改控件状态的代码,那么由于最终执行这个方法的是界面线程,从而避免了竞争条件,避免了不可预料的问题。如果其它线程直接操作界面线程所属的控件,那么将会产生竞争条件,造成不可预料的结果。使用Inv
11、oke完成一个委托方法的封送,就类似于使用SendMessage方法来给界面线程发送消息,是一个同步方法。也就是说在Invoke封送的方法被执行完毕前,Invoke方法不会返回,从而调用者线程将被阻塞。使用BeginInvoke方法封送一个委托方法,类似于使用PostMessage进行通信,这是一个异步方法。也就是该方法封送完毕后马上返回,不会等待委托方法的执行结束,调用者线程将不会被阻塞。但是调用者也可以使用EndInvoke方法或者其它类似WaitHandle机制等待异步操作的完成。但是在内部实现上,Invoke和BeginInvoke都是用了PostMessage方法,从而避免了Send
12、Message带来的问题。而Invoke方法的同步阻塞是靠WaitHandle机制来完成的。,使用场合问题,如果你的后台线程在更新一个UI控件的状态后不需要等待,而是要继续往下处理,那么你就应该使用BeginInvoke来进行异步处理。如果你的后台线程需要操作UI控件,并且需要等到该操作执行完毕才能继续执行,那么你就应该使用Invoke。否则,在后台线程和主截面线程共享某些状态数据的情况下,如果不同步调用,而是各自继续执行的话,可能会造成执行序列上的问题,虽然不发生死锁,但是会出现不可预料的显示结果或者数据处理错误。可以看到ISynchronizeInvoke有一个属性,InvokeRequi
13、red。这个属性就是用来在编程的时候确定,一个对象访问UI控件的时候是否需要使用Invoke或者BeginInvoke来进行封送。如果不需要那么就可以直接更新。在调用者对象和UI对象同属一个线程的时候这个属性返回false。在后面的代码分析中我们可以看到,Control类对这一属性的实现就是在判断调用者和控件是否属于同一个线程的。,代码(一)来看(Control的Invoke) private delegate void InvokeDelegate(); private void InvokeMethod() /C代码段 private void butInvoke_Click(object
14、 sender, EventArgs e) /A代码段. this.Invoke(new InvokeDelegate(InvokeMethod); /B代码段 解释:(1)A在UI线程上执行完后,开始Invoke,Invoke是同步 (2)代码段B并不执行,而是立即在UI线程上执行InvokeMethod方法,即代码段C。 (3)InvokeMethod方法执行完后,代码段C才在UI线程上继续执行。,代码(二),Control的BeginInvoke private delegate void BeginInvokeDelegate(); private void BeginInvokeMe
15、thod() /C代码段 private void butBeginInvoke_Click(object sender, EventArgs e) /A代码段. this.BeginInvoke(new BeginInvokeDelegate(BeginInvokeMethod); /B代码段 解释:(1)A在UI线程上执行完后,开始BeginInvoke,BeginInvoke是异步 (2)InvokeMethod方法,即代码段C不会执行,而是立即在UI线程上执行代码段B。 (3)代码段B执行完后(就是说butBeginInvoke_Click方法执行完后),InvokeMethod方法,
16、即代码段C才在UI线程上继续执行。,代码(三) Thread调用Control的Invoke private Thread invokeThread; private delegate void invokeDelegate(); private void StartMethod() /C代码段 Control.Invoke(new invokeDelegate(invokeMethod); /D代码段 private void invokeMethod() /E代码段 private void butInvoke_Click(object sender, EventArgs e) /A代码段
17、. invokeThread = new Thread(new ThreadStart(StartMethod); invokeThread.Start(); /B代码段 解释: 1。UI执行A 2。UI开线程InvokeThread,B和C同时执行,B执行在线程UI上,C执行在线程invokeThread上。 3。invokeThread封送消息给UI,然后自己等待,UI处理完消息后,处理invokeThread封送的消息,即代码段E 4。UI执行完E后,转到线程invokeThread上,invokeThread线程执行代码段D,private Thread beginInvokeThre
18、ad; private delegate void beginInvokeDelegate(); private void StartMethod() /C代码段 Control.BeginInvoke(new beginInvokeDelegate(beginInvokeMethod); /D代码段 private void beginInvokeMethod() /E代码段 private void butBeginInvoke_Click(object sender, EventArgs e) /A代码段. beginInvokeThread = new Thread(new Threa
19、dStart(StartMethod); beginInvokeThread .Start(); /B代码段 解释: 1。UI执行A 2。UI开线程beginInvokeThread,B和C同时执行,B执行在线程UI上,C执行在线程beginInvokeThread上。 3。beginInvokeThread封送消息给UI,然后自己继续执行代码D,UI处理完消息后,处理invokeThread封送的消息,即代码段E 有点疑问:如果UI先执行完毕,是不是有可能过了段时间beginInvokeThread才把消息封送给UI,然后UI才继续执行封送的消息E。,Delegate.BeginInvoke
20、,通过一个委托来进行同步方法的异步调用,也是.net提供的异步调用机制之一。但是Delegate.BeginInvoke方法是从ThreadPool取出一个线程来执行这个方法,以获得异步执行效果的。也就是说,如果采用这种方式提交多个异步委托,那么这些调用的顺序无法得到保证。而且由于是使用线程池里面的线程来完成任务,使用频繁,会对系统的性能造成影响。 Delegate.BeginInvoke也是讲一个委托方法封送到其它线程,从而通过异步机制执行一个方法。调用者线程则可以在完成封送以后去继续它的工作。但是这个方法封送到的最终执行线程是运行库从ThreadPool里面选取的一个线程。 这里需要纠正一
21、个误区,那就是Control类上的异步调用BeginInvoke并没有开辟新的线程完成委托任务,而是让界面控件的所属线程完成委托任务的。看来异步操作就是开辟新线程的说法不一定准确。终于看到了,这是在判断windows窗体线程和当前的调用者线程是否是同一个,如果是同一个就没有必要封送了,直接访问这个GUI控件吧。否则,就不要那么直接表白了,就需要Invoke或者BeginInvoke做媒了。,C#多线程 Invoke方法的使用,在多线程编程中,我们经常要在工作线程中去更新界面显示,而在多线程中直接调用界面控件的方法是错误的做法,Invoke 和 BeginInvoke 就是为了解决这个问题而出现
22、的,使你在多线程中安全的更新界面显示。正确的做法是将工作线程中涉及更新界面的代码封装为一个方法,通过 Invoke 或者 BeginInvoke 去调用,两者的区别就是一个导致工作线程等待,而另外一个则不会。而所谓的“一面响应操作,一面添加节点”永远只能是相对的,使 UI 线程的负担不至于太大而已,因为界面的正确更新始终要通过 UI 线程去做,我们要做的事情是在工作线程中包揽大部分的运算,而将对纯粹的界面更新放到 UI 线程中去做,这样也就达到了减轻 UI 线程负担的目的了。再举个简单例子说明下使用方法,比如你在启动一个线程,在线程的方法中想更新窗体中的一个TextBox,using Syst
23、em.Threading; public delegate void MyInvoke(string str); private void btnStartThread_Click(object sender, EventArgs e) Thread thread = new Thread(new ThreadStart(DoWord); thread.Start(); public void DoWord() MyInvoke mi = new MyInvoke(SetTxt);BeginInvoke(mi,new object“abc“); public void SetTxt(string str) txtReceive.Text += str + System.Environment.NewLine; ,