很多时候写windows程序都需要结合多线程,在C#中用如下得代码来创建并启动一个新的线程。

  1. Thread thread = new Thread(new ThreadStart(ThreadProc));//实例化一个线程
  2.  
  3. thread.IsBackground = true;//将线程改为后台线程
  4.  
  5. thread.Start();//开启线程

但是很多时候,在新的线程中,我们需要与UI(Windows窗体设计器用户界面)进行交互,在C#中不允许直接这样做。可以参考MSDN中的描述。

“Windows 窗体”使用单线程单元 (STA) 模型,因为“Windows 窗体”基于本机Win32窗口,而Win32窗口从本质上而言是单元线程。STA模型意味着可以在任何线程上创建窗口,但窗口一旦创建后就不能切换线程,并且对它的所有函数调用都必须在其创建线程上发生。除了Windows窗体之外,.NET Framework 中的类使用自由线程模型。

STA模型要求需从控件的非创建线程调用的控件上的任何方法必须被封送到(在其上执行)该控件的创建线程。基类Control为此目的提供了若干方法(Invoke、BeginInvoke 和 EndInvoke)。Invoke生成同步方法调用;BeginInvoke生成异步方法调用。

Windows窗体中的控件被绑定到特定的线程,不具备线程安全性。因此,如果从另一个线程调用控件的方法,那么必须使用控件的一个Invoke方法来将调用封送到适当的线程。

正如所看到的,必须调用Invoke方法,而BeginInvoke可以认为是Invoke的异步版本。调用方法如下:

  1. public delegate void OutDelegate(string text);
  2.  
  3. public void OutText(string text)
  4.  
  5. {
  6.  
  7. txt.AppendText(text);
  8.  
  9. txt.AppendText( "\t\n" );
  10.  
  11. }
  12.  
  13. OutDelegate outdelegate = new OutDelegate( OutText );
  14.  
  15. this.BeginInvoke(outdelegate, new object[]{text});

如果需要在另外一个线程里面对UI进行操作,需要一个类似OutText的函数,还需要一个该函数的委托delegate,当然,这里展示的是自定义的。

该属性可用于确定是否必须调用 Invoke 方法,当不知道什么线程拥有控件时这很有用。

也就是说通过判断InvokeRequired可以知道是否需要用委托来调用当前控件的一些方法,如此可以把OutText函数修改一下:

  1. public delegate void OutDelegate(string text);
  2.  
  3. public void OutText(string text)
  4.  
  5. {
  6.  
  7. if( txt.InvokeRequired )
  8.  
  9. {
  10.  
  11. OutDelegate outdelegate = new OutDelegate( OutText );
  12.  
  13. this.BeginInvoke(outdelegate, new object[]{text});
  14.  
  15. return;
  16.  
  17. }
  18.  
  19. txt.AppendText(text);
  20.  
  21. txt.AppendText( "\t\n" );
  22.  
  23. }

注意,这里的函数没有返回,如果有返回,需要调用Invoke或者EndInvoke来获得返回的结果,不要因为包装而丢失了返回值。如果调用没有完成,Invoke和EndInvoke都将会引起阻塞。

现在如果我有一个线程函数如下:

  1. public void ThreadProc()
  2.  
  3. {
  4.  
  5. for(int i = ; i < ; i++)
  6.  
  7. {
  8.  
  9. OutText( i.ToString() );
  10.  
  11. Thread.Sleep();
  12.  
  13. }
  14.  
  15. }

如果循环的次数很大,或者漏了Thread.Sleep(1000);,那么你的UI肯定会停止响应,想知道原因吗?看看BeginInvoke前面的对象,没错,就是this,也就是主线程,当你的主线程不停的调用OutText的时候,UI当然会停止响应。

与以前VC中创建一个新的线程需要调用AfxBeginThread函数,该函数中第一个参数就是线程函数的地址,而第二个参数是一个类型为LPVOID的指针类型,这个参数将传递给线程函数。现在我们没有办法再使用这种方法来传递参数了。我们需要将传递给线程的参数和线程函数包装成一个单独的类,然后在这个类的构造函数中初始化该线程所需的参数,然后再将该实例的线程函数传递给Thread类的构造函数。代码大致如下:

  1. public class ProcClass
  2.  
  3. {
  4.  
  5. private string procParameter = "";
  6.  
  7. public ProcClass(string parameter)
  8.  
  9. {
  10.  
  11. procParameter = parameter;
  12.  
  13. }
  14.  
  15. public void ThreadProc()
  16.  
  17. {
  18.  
  19. }
  20.  
  21. }
  22.  
  23. ProcClass threadProc = new ProcClass("use thread class");
  24.  
  25. Thread thread = new Thread( new ThreadStart( threadProc.ThreadProc ) );
  26.  
  27. thread.IsBackground = true;
  28.  
  29. thread.Start();

就是这样,需要建立一个中间类来传递线程所需的参数。

那么如果我的线程又需要参数,又需要和UI进行交互的时候该怎么办呢?可以修改一下代码:

  1. public class ProcClass
  2.  
  3. {
  4.  
  5. private string procParameter = "";
  6.  
  7. private Form1.OutDelegate delg = null;
  8.  
  9. public ProcClass(string parameter, Form1.OutDelegate delg)
  10.  
  11. {
  12.  
  13. procParameter = parameter;
  14.  
  15. this.delg = delg;
  16.  
  17. }
  18.  
  19. public void ThreadProc()
  20.  
  21. {
  22.  
  23. delg.BeginInvoke("use ProcClass.ThreadProc()", null, null);
  24.  
  25. }
  26.  
  27. }
  28.  
  29. ProcClass threadProc = new ProcClass("use thread class", new OutDelegate(OutText));
  30.  
  31. Thread thread = new Thread( new ThreadStart( threadProc.ThreadProc ) );
  32.  
  33. thread.IsBackground = true;
  34.  
  35. thread.Start();

C#中线程的委托的更多相关文章

  1. Cocos2dx中线程优先级

    Cocos2dx中线程优先级问题 不论是ios还是android,遇到耗时的任务都要另起线程处理,否则程序不能及时用户的反馈.游戏中如果一圈循环不能在1/frameRate(帧率是30则1/30)秒内 ...

  2. java中线程分两种,守护线程和用户线程。

    java中线程分为两种类型:用户线程和守护线程. 通过Thread.setDaemon(false)设置为用户线程: 通过Thread.setDaemon(true)设置为守护线程. 如果不设置次属性 ...

  3. Java中线程池的学习

    线程池的基本思想还是一种对象池的思想,开辟一块内存空间,里面存放了众多(未死亡)的线程,池中线程执行调度由池管理器来处理.当有线程任务时,从池中取一个,执行完成后线程对象归池,这样可以避免反复创建线程 ...

  4. boost中asio网络库多线程并发处理实现,以及asio在多线程模型中线程的调度情况和线程安全。

    1.实现多线程方法: 其实就是多个线程同时调用io_service::run for (int i = 0; i != m_nThreads; ++i)        {            boo ...

  5. java中线程机制

    java中线程机制,一开始我们都用的单线程.现在接触到多线程了. 多线性首先要解决的问题是:创建线程,怎么创建线程的问题: 1.线程的创建: 四种常用的实现方法 1.继承Thread. Thread是 ...

  6. worker进程中线程的分类及用途

    worker进程中线程的分类及用途 欢迎转载,转载请注明出版,徽沪一郎. 本文重点分析storm的worker进程在正常启动之后有哪些类型的线程,针对每种类型的线程,剖析其用途及消息的接收与发送流程. ...

  7. Java多线程中线程间的通信

    一.使用while方式来实现线程之间的通信 package com.ietree.multithread.sync; import java.util.ArrayList; import java.u ...

  8. Java中线程的使用 (2)-多线程、线程优先级、线程睡眠、让步、阻塞

    Java中线程的使用 (2)-多线程.线程优先级.线程睡眠.让步.阻塞 (一)多线程使用方法 说明:创建每个新的线程,一定要记得启动每个新的线程(调用.start()方法) class Xc3 ext ...

  9. Java中线程的实现:

    Java中线程的实现: 一.线程简介: 实现的两种方式为: 1.Thread类 2.Runnable接口 都在java.lang中 都有共通的方法:public void run() 二.线程常用方法 ...

随机推荐

  1. jetty中war包解压路径

    这是个很奇怪的问题,如果下载好了jetty直接放入war包运行,项目会被解压到C盘的临时文件夹中.但是如果你在${JETTY_HOME}文件夹,也就是jetty解压后的根目录中新建,注意是新建一个wo ...

  2. 在mac上 使用jenkins 执行python文件

    1.要选择 [执行 shell]构建

  3. unittest框架+ HTMLTestRunner 出报告时,展示控制台信息 不同展示的参数写法 加verbosity

    加verbosity参数 没有加的时候展示: 参考: 来源:  https://www.cnblogs.com/tomweng/p/6609937.html 介绍: HTMLTestRunner 是 ...

  4. 工具软件集合 Adobe AE PS Pr CC 2018 2019 破解教程

    来源https://mp.weixin.qq.com/s/zeq1sTmaPsKt7Bsok0Ldrg(若链接失效,请关注软件安装管家公众号) 相关链接 Office 2019破解教程 Adobe 2 ...

  5. Idea 如何设置微软雅黑等其它字体

    使用过idea的同学都知道,idea的功能相对于explise来说,功能太强大了啊~个人感觉,idea真心挺智能的.但是,这里有一个小瑕疵,就是能够设置的字体,有限! 对于用惯了 微软雅黑 字体的人, ...

  6. python学习(六) 抽象

    6.1 懒惰即美德 斐波那契数列: >>> fabs = [0, 1]>>> for i in range(8): fabs.append(fabs[-1] + f ...

  7. python2 encode和decode函数说明

    字符串编码常用类型:utf-8,gb2312,cp936,gbk等. python中,我们使用decode()和encode()来进行解码和编码 在python中,使用unicode类型作为编码的基础 ...

  8. 安全测试之session,cookie

    session session机制是一种服务器端的机制,服务器使用一种类似于散列表的结构(也可能就是使用散列表)来保存信息.•但程序需要为某个客户端的请求创建一个session的时候,服务器首先检查这 ...

  9. 关于XSS漏洞的简介以及分类

    不得不说注入的时代已经过去了,最近xss貌似比较热门.我就去恶补了一下,我表示我只是菜鸟,对xss不了解.所以从最基本的学起. 什么xss漏洞? 一.XSS攻击简介 作为一种HTML注入攻击,XSS攻 ...

  10. 【转】OpenGL随笔(1)—— mipmap 详解

    注:本文使用的所有 OpenGL 函数来自 OpenGL 4.5,优先使用 DSA. 使用 mipmap 时,OpenGL 根据被映射对象的大小(单位是像素),自动决定使用纹理图的哪个分辨率级别.mi ...