当我们需要处理大量数据时,为了使UI界面不致出现假死状态,我们就必须使用多线程进行处理。所以问题就出现了,我们都知道线程作为一个独立运行的单元,线程间不可以随意访问和修改,那么该怎么办呢?其实C#提供了跨线程访问的方法,也就是通过委托安全调用从非拥有控件的线程访问控件。
一、委托 我们首先先来了解下委托,简单地说,委托就是一个类,它定义了方法传递参数的类型和个数,使得我们可以把方法作为参数进行传递,使得程序具有更好的扩展性。如果大家还不明白的话,我们可以举个例子:
private delegate void setTextDelegate( string msg); //声明一个委托类型,这个委托类型传递一个string类型参数,并返回为void private void setLabelText(string value) //这是一个传递string类型参数,并返回void的方法 { this.label1.Text = value; } private void setTextboxTex(string msg) //这也是一个传递string类型参数,并返回void的方法 { this.textBox1.Text = msg; } private void setText(string msg,setTextDelegate std) //把setTextDelegate类型委托作为一个参数进行传递 { std(msg); //真正的参数传递 } private void button1_Click(object sender, EventArgs e) { this.setText("这是label控件", setLabelText); //因为setLabelText传递的参数类型和返回值都和委托声明的类型一致,所以可以进行传递 this.setText("这是textBox控件", setTextboxTex); //同上 }
总结下,从上面的例子我们可以清楚地看到,只要方法传递的参数类型、个数和返回值与委托声明的一致,我们就可以把该方法作为参数进行传递。
我们也可以将多个方法绑定到委托上,所有绑定上去的方法就会形成一个链表顺序执行。
private void button1_Click(object sender, EventArgs e) { setTextDelegate std = new setTextDelegate(setLabelText); //实例化委托并绑定setLabelText方法 std += setTextboxTex; //使用"+="号绑定方法,解绑使用"-="号 this.setText("这是控件", std); }
这样setTextDelegate绑定了两个方法,当我们this.setText("这是控件", std);时两个方法会顺序执行,这样做大大提高了程序的可扩性。
在讲解跨线程委托调用的方法前,我们先了解几个常用的方法和属性:
1.Control.InvokeRequired属性(Control是所有控件的基类),这个属性用来判断Control控件是否为调用线程创建的,如果为否的话,也就是创建Control控件的线程不是调用线程,返回false,否则返回true。
2.Control.Invoke() 方法,这是同步调用的方法,它顺序执行Invoke(Delegate)里的委托方法会再继续执行下面的方法,下面会详细解释。
3.Control.BeginInvoke()方法,这是异步调用的方法,它会类似于把委托内的方法又创建了一条线程来执行,只有调用线程进行Sleep切换时才会执行委托方法,下面会详细解释它和Control.Invoke()方法的区别。
先来看个例子:
private delegate void setTextDelegate( int value); //先声明一个传递int类型参数,并返回为void的委托 private void button1_Click(object sender, EventArgs e) { Thread newThread = new Thread(new ThreadStart(threadHandler)); newThread.Start(); } private void threadHandler() { for(int i =0 ; i <=100 ; i ++) { this.UIHandler(i); Thread.Sleep(100); } } private void UIHandler(int value) { if(this.label1.InvokeRequired) //判断label1控件是否是调用线程(即newThread线程)创建的,也就是是否跨线程调用,如果是则返回true,否则返回false { this.label1.BeginInvoke(new setTextDelegate(setLabelText),new object []{ value}); //异步调用setLabelText方法,并传递一个int参数 } else { this.label1.Text = value.ToString() + "%"; } } private void setLabelText(int value) //当跨线程调用时,调用该方法进行UI界面更新 { this.label1.Text = value.ToString() + "%"; }
这是一个简单的跨线程调用的例子,不懂的可以把例子拷贝自己运行下就清楚了,其实原理很简单,分两步就搞定了:
1. 声明一个委托类型,定义它需要传递的参数类型、个数和返回的类型。
2.判断是否跨线程调用更新控件内容(Control.InvokeRequired),如果是的话,就要用Invoke()或BeginInvoke()执行委托。
三、Control.Invoke()与Control.BeginInvoke()方法的区别我们都知道这两个方法都可以进行委托执行,但其中Control.Invoke()是同步调用执行,Control.BeginInvoke()是异步调用执行。
MSDN上的解释是这样的:
Control.Invoke()方法:在拥有此控件的基础窗口句柄的线程上执行委托。 Control.BeginInvoke()方法:在创建控件的基础句柄所在线程上异步执行委托。
从MSDN的解释我们可以得出,委托都是在Control线程执行的,也就是UI主线程执行的。两者的区别主要是异步执行和顺序执行的区别,也就是执行顺序的不一致。
下面举个例子来看就明白了,就拿上面的例子来讲解好了。
private delegate void setTextDelegate( int value); //先声明一个传递int类型参数,并返回为void的委托 private void button1_Click(object sender, EventArgs e) { Thread newThread = new Thread(new ThreadStart(threadHandler)); newThread.Start(); } private void threadHandler() { for(int i =0 ; i <=100 ; i ++) { this.UIHandler(i); Thread.Sleep(100); } } private void UIHandler(int value) { // ........① if(this.label1.InvokeRequired) //判断label1控件是否是调用线程(即newThread线程)创建的,也就是是否跨线程调用,如果是则返回true,否则返回false { this.label1.Invoke(new setTextDelegate(setLabelText),new object []{ value}); //同步调用......② //this.label1.BeginInvoke(new setTextDelegate(setLabelText),new object []{ value}); //异步调用 ......③ } else { this.label1.Text = value.ToString() + "%"; //.......④ } //.........⑤ } private void setLabelText(int value) //当跨线程调用时,调用该方法进行UI界面更新 { this.label1.Text = value.ToString() + "%"; //..........⑥ }大家先猜猜看,如果是Invoke调用的话,也就是(①、②、④、⑤、⑥)的执行顺序,而如果是BeginInvoke调用的话,也就是(①、③、④、⑤、⑥)的执行顺序。
正确的执行顺序:Invoke是①->②->⑥-->⑤,而BeginInvoke是①->③->⑤->⑥,④这步不会执行到。
大家可以自己去试下,其实原理我上面已经说过了,使用Invoke方法的话,调用线程会马上进行切换操作,切换到Control控件所在线程(UI线程)进行界面的更新后,再切换回来继续执行下面的内容,而BeginInvoke方法的话,调用线程不会马上进行切换操作,它会在Thread.Sleep(100);的时候进行切换到UI线程进行界面的更新,所以用BeginInvoke方法的时候一定要注意,每次执行完要Thread.Sleep(100);下让界面更新,否则界面还是不会有变化的。
四、委托的类型.Net Framework 提供了两种类型的委托,一种是自定义型的,一种是系统定义型的(固定类型)。自定义型的就不说了,也就是需要自己进行声明委托类型,灵活性高,不过系统默认提供的几个固定类型的委托也可以让我们快速实现委托。
1. public delegate void MethodInvoker () //声明返回值为 void 且不接受任何参数传递的任何方法
private void button1_Click(object sender, EventArgs e) { Thread newThread = new Thread(new ThreadStart(threadHandler)); newThread.Start(); } private void threadHandler() { if(this.label1.InvokeRequired) //判断label1控件是否是调用线程(即newThread线程)创建的,也就是是否跨线程调用,如果是则返回true,否则返回false { this.label1.Invoke(new MethodInvoker(threadHandler)); //把封包发送UI线程,即UI线程执行threadHandler方法 } else { this.label1.Text = "这是lable控件"; //第二次this.label1.InvokeRequired判断后会执行到这里 } }2. public delegate void EventHandler ( Object sender, EventArgs e) //表示将处理不包含事件数据的事件的方法。
private void button1_Click(object sender, EventArgs e) { Thread newThread = new Thread(new ThreadStart(threadHandler)); newThread.Start(); } private void threadHandler() { if(this.label1.InvokeRequired) //判断label1控件是否是调用线程(即newThread线程)创建的,也就是是否跨线程调用,如果是则返回true,否则返回false { this.label1.Invoke(new EventHandler(setLabelText),new object[]{"这是lable控件"}); //EventHandler可以传递object参数 } else { this.label1.Text = "这是lable控件"; } } private void setLabelText(object o, System.EventArgs e) { this.label1.Text = o.ToString(); }后述:委托的内容就讲到这吧,讲得不对不好的地方请大家多多包涵。
更多详细信息请查看java教程网 http://www.itchm.com/forum-59-1.html
相关推荐
本文将深入探讨一个基于C#的TCP服务实现,它利用了多线程、异步编程、拉姆达表达式、UI跨线程更新以及递归等技术,以实现一个能够支持多个客户端的异步聊天通讯系统。 首先,TCP(传输控制协议)是一种面向连接的、...
综上所述,C#接口API通讯涉及多个方面,包括串口通信的实现、API调用、线程安全、委托、异步编程以及错误处理。理解并掌握这些知识点,将有助于你开发出高效且可靠的跨平台通信解决方案。在实际项目中,应遵循最佳...
总的来说,这个C#项目结合了多线程、异步编程、委托与lambda表达式、UI跨线程操作和递归等关键技术,提供了一个全面的学习案例,帮助开发者理解和实践这些重要概念。通过分析和研究这个源码,初学者不仅可以了解这些...
在本文中,我们将深入探讨如何使用C#...以上就是关于“德国SICK RFID读卡器RFU630 读卡程序 TCP客户端 C# 异步读取 多线程 使用一句话建立委托实现跨线程访问控件”这一主题的详细说明,希望对你的理解和实践有所帮助。
总结,C# .NET 2.0即时通讯源码涉及到网络编程、协议设计、多线程、数据序列化、安全通信等多个技术领域,需要深入理解并熟练运用这些技术,才能构建出稳定高效的即时通讯系统。LanMsgC#2.1.3可能就是这样一个实现,...
C# Remoting虽然功能强大,但由于.NET Framework 4.0后被WCF取代,所以在新项目中,你可能需要考虑使用WCF或更现代的技术如gRPC、WebSocket等实现跨进程通信。不过,对于已有的基于C# Remoting的系统,理解和掌握其...
TCP通讯 ● 串口通讯等要素,并通过C#相关技术如数据类型 抽象 继承 事件event 函数 修饰符(public private internal)泛型 Action委托 IPAddress 多态函数 socket编程(tcpclient)SerialPort串口多线程 ...
在C#中,这通常通过线程、委托或事件处理来实现。 8. **关闭连接和释放资源**: 完成NFC操作后,记得调用`nfc_close()`关闭设备连接,`nfc_exit()`清理libNFC上下文,避免内存泄漏。 9. **异常处理**: C#中的异常...
3. **C#编程**:掌握C#的基本语法,如类、对象、事件、委托等,熟悉.NET Framework中的IO操作、线程管理和异常处理机制,这些都是编写驱动程序的基础。 4. **多线程编程**:由于CAN通信通常是实时的,可能需要同时...
EventBus常用于UI事件、跨组件通信、微服务之间的通讯等多个场景,尤其在响应式编程和领域驱动设计(DDD)中扮演重要角色。 通过以上知识点,我们可以构建一个功能完善的C#版本EventBus事件总线实例,为应用程序...
TCP异步通信的核心在于使用Socket的BeginConnect、BeginReceive和BeginSend等方法,这些方法采用回调机制,在操作完成后调用预先定义的委托方法,而不是阻塞当前线程等待操作完成。这种方式可以避免线程被长时间占用...
在这个示例代码中,我们将探讨如何使用 C# 实现跨进程访问,并分析该代码中的关键技术点。 一、跨进程访问的概念 跨进程访问是指在不同的应用程序域、进程或 threads 之间共享资源或数据的机制。这可以实现不同...
6. 多线程编程:为了实现高效的并发处理,飞信客户端可能使用多线程技术,确保用户在接收新消息的同时可以进行其他操作。 7. UI设计:飞信C#源码中包含了用户界面的设计,这涉及到Windows Forms或WPF(Windows ...
- 创建Socket实例,设置事件处理委托,并启动接收数据的线程。 - 当数据到达时,通过委托调用处理函数,实现事件响应。 通过以上步骤,我们可以在C#应用中,利用委托机制实现与C++动态库的交互,从而达到Socket ...
用C#来开发这样的应用,开发者可能希望利用C#的跨平台特性(通过.NET Core)来吸引不同操作系统(如Windows、macOS、Linux)的用户。 基于以上信息,我们可以探讨以下C#开发MYQQ时可能涉及的知识点: 1. **C#基础...
可以使用委托和事件来处理消息传递,使得当新的消息到达时,界面能够实时更新。 6. **通信源代码**:通信源代码主要包括网络连接的建立、保持和断开,数据包的打包和解析,以及错误处理机制。在C#中,这些可以通过...
C#作为一种高效、跨平台的编程语言,常被用于开发此类应用。本资源提供的是一套基于C#的企业内部QQ源码,对于想要学习如何构建类似系统或提升C#编程能力的开发者来说,具有极高的参考价值。 1. **C#语言基础** C#...
C#的特点包括类、接口、委托、事件、属性、枚举、泛型、匿名方法、Lambda表达式等。C#还支持Windows桌面应用、Web应用、游戏开发(通过Unity引擎)以及移动应用开发(如UWP和Xamarin)。 **网络:** 网络编程涵盖了...
如果窗体操作涉及UI更新,确保事件处理程序运行在正确的线程上,以免出现跨线程访问控件导致的异常。可以使用`Control.Invoke`或`Control.BeginInvoke`方法确保UI更新在UI线程中执行。 总之,委托是.NET中实现对象...
8. **事件驱动编程**:即时通讯系统往往基于事件驱动模型,例如消息接收事件、连接状态变化事件等,C#中的事件和委托机制能很好地支持这种模式。 9. **异常处理**:良好的错误处理是任何软件的关键部分,源代码中应...