C#线程委托实现原理及方法解析

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

Thread thread = new Thread(new ThreadStart(ThreadProc));//实例化一个线程

thread.IsBackground = true;//将线程改为后台线程

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的异步版本。调用方法如下:

public delegate void OutDelegate(string text);
public void OutText(string text)
{
   txt.AppendText(text);
   txt.AppendText( "\t\n" );
}
OutDelegate outdelegate = new OutDelegate( OutText );
this.BeginInvoke(outdelegate, new object[]{text});

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

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

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

public delegate void OutDelegate(string text);
public void OutText(string text)
{
   if( txt.InvokeRequired )
   {
     OutDelegate outdelegate = new OutDelegate( OutText );
     this.BeginInvoke(outdelegate, new object[]{text});
     return;
   }
   txt.AppendText(text);
   txt.AppendText( "\t\n" );
}

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

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

public void ThreadProc()
{
   for(int i = 0; i < 5; i++)
   {
     OutText( i.ToString() );
     Thread.Sleep(1000);
   }
}

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

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

public class ProcClass
{
   private string procParameter = "";
   public ProcClass(string parameter)
   {
     procParameter = parameter;
   }
   public void ThreadProc()
   {
   }
}
ProcClass threadProc = new ProcClass("use thread class");
Thread thread = new Thread( new ThreadStart( threadProc.ThreadProc ) );
thread.IsBackground = true;
thread.Start();

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

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

public class ProcClass
{
   private string procParameter = "";
   private Form1.OutDelegate delg = null;
   public ProcClass(string parameter, Form1.OutDelegate delg)
   {
     procParameter = parameter;
     this.delg = delg;
   }
   public void ThreadProc()
   {
     delg.BeginInvoke("use ProcClass.ThreadProc()", null, null);
   }
}
ProcClass threadProc = new ProcClass("use thread class", new OutDelegate(OutText));
Thread thread = new Thread( new ThreadStart( threadProc.ThreadProc ) );
thread.IsBackground = true;
thread.Start();

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • 基于C#实现的轻量级多线程队列图文详解

    前言 工作中我们经常会遇到一些一些功能需要实现造作日志,数据修改日志,对于这种业务需求如果我们以同步的方式实现,难免会影响到系统的性能.如下我列出集中解决方案. 使用Thread异步处理. 使用线程池或Task异步处理. 以上两种方案确实能解决我们此场景的需求,但是同时也带来了问题. 第一种方式,使用thread的情况下我们无法控制创建的线程数量,要知道创建线程是一个很耗性能的操作. 第二种方式,使用线程池或者Task我们虽然可以通过设置线程池的最大线程数量来限制线程最大数,但是这个设置由于是全

  • 深入分析C# 线程同步

    上一篇介绍了如何开启线程,线程间相互传递参数,及线程中本地变量和全局共享变量区别. 本篇主要说明线程同步. 如果有多个线程同时访问共享数据的时候,就必须要用线程同步,防止共享数据被破坏.如果多个线程不会同时访问共享数据,可以不用线程同步. 线程同步也会有一些问题存在: 性能损耗.获取,释放锁,线程上下文建切换都是耗性能的. 同步会使线程排队等待执行. 线程同步的几种方法: 阻塞 当线程调用Sleep,Join,EndInvoke,线程就处于阻塞状态(Sleep使调用线程阻塞,Join.EndIn

  • C#用委托BeginInvoke做异步线程

    一个应用场景,浏览器上传一个文件,此文件后台调用文件转换,需要耗费相当长的时间,这样,如果是一个线程同步式的做下去,那么用户在浏览器上感觉就是卡住了,卡卡卡卡,这里我们利用委托的BeginInvoke和EndInvoke方法操作线程,BeginInvoke方法可以使用线程异步地执行委托所指向的方法.然后通过EndInvoke方法获得方法的返回值(EndInvoke方法的返回值就是被调用方法的返回值),或是确定方法已经被成功调用,说白了就是相当于开个多线程,你用户文件保存了之后,响应返回,这个Be

  • 详解c# 线程同步

    一.线程同步概述 前面的文章都是讲创建多线程来实现让我们能够更好的响应应用程序,然而当我们创建了多个线程时,就存在多个线程同时访问一个共享的资源的情况,在这种情况下,就需要我们用到线程同步,线程同步可以防止数据(共享资源)的损坏. 然而我们在设计应用程序还是要尽量避免使用线程同步, 因为线程同步会产生一些问题: 1. 它的使用比较繁琐.因为我们要用额外的代码把多个线程同时访问的数据包围起来,并获取和释放一个线程同步锁,如果我们在一个代码块忘记获取锁,就有可能造成数据损坏. 2. 使用线程同步会影

  • C# 线程相关知识总结

    初识线程 线程是一个独立的运行单元,每个进程内部都有多个线程,每个线程都可以各自同时执行指令.每个线程都有自己独立的栈,但是与进程内的其他线程共享内存.但是对于.NET的客户端程序(Console,WPF,WinForms)是由CLR创建的单线程(主线程,且只创建一个线程)来启动.在该线程上可以创建其他线程. 图: 线程工作方式 多线程由内部线程调度程序管理,线程调度器通常是CLR委派给操作系统的函数.线程调度程序确保所有活动线程都被分配到合适的执行时间,线程在等待或阻止时 (例如,在一个独占锁

  • C#中的多线程小试牛刀

    前言 昨天在上班时浏览博问,发现了一个问题,虽然自己在 C# 多线程上没有怎么尝试过,看了几遍 CLR 中关于 线程的概念和讲解(后面三章).也想拿来实践实践.问题定义是这样的: 对于多线程不是很懂,面试的时候遇到一个多线程的题,不会做,分享出来,懂的大佬指点一下,谢谢 建一个winform窗体,在窗体中放上一个开始按钮,一个停止按钮,一个文本框,在窗体中声明一个List类型的属性,点击开始按钮后开启10个线程,所有线程同时不间断的给List集合中添加1-10000之间的随机数,要求添加List

  • C# 多线程编程技术基础知识入门

    什么是进程? 当一个程序开始运行时,它就是一个进程,进程包括运行中的程序和程序所使用到的内存和系统资源.而一个进程又是由多个线程所组成的. 什么是线程? 线程是程序中的一个执行流,每个线程都有自己的专有寄存器(栈指针.程序计数器等),但代码区是共享的,即不同的线程可以执行同样的函数. 什么是多线程? 多线程是指程序中包含多个执行流,即在一个程序中可以同时运行多个不同的线程来执行不同的任务,也就是说允许单个程序创建多个并行执行的线程来完成各自的任务. 多线程是指程序中包含多个执行流,即在一个程序中

  • C# 线程同步的方法

    一.进程内部的线程同步 1.使用lock,用法如下: private static readonly object SeqLock = new object(); private void Print() { lock (SeqLock) { Console.WriteLine("test"); } } 特性:只能传递对象,无法设置等待超时 2.使用:InterLocked(原子操作) 其在System.Threading命名空间下,Interlocked实际是类控制计数器,从而实现进

  • C#线程委托实现原理及方法解析

    很多时候写windows程序都需要结合多线程,在C#中用如下得代码来创建并启动一个新的线程. Thread thread = new Thread(new ThreadStart(ThreadProc));//实例化一个线程 thread.IsBackground = true;//将线程改为后台线程 thread.Start();//开启线程 但是很多时候,在新的线程中,我们需要与UI(Windows窗体设计器用户界面)进行交互,在C#中不允许直接这样做.可以参考MSDN中的描述. "Wind

  • Linux磁盘分区实现原理及方法解析

    回忆: IDE盘:第一块盘hda,第二块盘hdb... 第一块盘的第一个分区hda1,第二个分区hda2... SAS/SATA/SCSI盘:第一块盘sda,第二块盘sdb... 第一块盘的第一个分区sda1,第二个分区sda2... 一般分区位于 /dev/sda1 这个位置. 什么是分区 磁盘分区就相当于给磁盘打隔断. 磁盘分区类型 1.主分区( primary)P 1)系统中必须要存在的分区,系统盘选择主分区安装 2)数字编号只能是1-4.sda1.sda2.sda3.sda4 3)主分区

  • Mysql临时表原理及创建方法解析

    这篇文章主要介绍了Mysql临时表原理及创建方法解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 mysql 利用 temporary 关键字就可以创建出一个临时表.创建的这张表会在与服务器的会话终止时自动消失 语法:create temporary table tbl_name...; 规则:每个会话只能看到自己创建的临时表,不同的会话可以创建相同表名称的临时表.临时表的表名可以和永久表的名字相同. 好处:可以利用临时表保存一些临时数据,断

  • Java线程状态运行原理解析

    这篇文章主要介绍了Java线程状态运行原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 代码实例如下 package com.fgy.demo05; /** * 等待唤醒案例:线程之间通信 * 注意: * 同步使用的锁对象必须唯一 * 只有锁对象才能调用wait和notify()/notifyAll()方法 */ public class Demo1WaitAndNotify { public static void main(Strin

  • java线程池中Worker线程执行流程原理解析

    目录 引言 Worker类分析 runWorker(Worker)方法 getTask()方法 beforeExecute(Thread, Runnable)方法 afterExecute(Runnable, Throwable)方法 processWorkerExit(Worker, boolean)方法 tryTerminate()方法 terminated()方法 引言 在<[高并发]别闹了,这样理解线程池执行任务的核心流程才正确!!>一文中我们深度分析了线程池执行任务的核心流程,在Th

  • Java线程中start和run方法全面解析

    自定义线程两种方法 自定义一个runnable接口的实现类,然后构造一个thread,即对thread传入一个runnable接口类. new一个thread或者写个thread子类,覆盖它的run方法.(new 一个thread并覆盖run方法实际上是匿名内部类的一种方式) 示例代码 public static void main(String[] args) { new Thread(new Runnable() { @Override public void run() { System.

  • 深入解析JS实现3D标签云的原理与方法

    本文实例讲述了深入解析JS实现3D标签云的原理与方法.分享给大家供大家参考,具体如下: 最近开始用canvas搞3D了,搞得也是简单的东西,就是球体转圈.做出来后,突然想起以前看过的3D标签云,在以前觉得真心狂拽酷炫叼啊,当时也确实不知道怎么在平面上模拟3D,所以也就没去搞了.现在刚好用了canvas搞3D,也发现,好像3D标签云也差不多,然后就写了一下. 具体怎么做呢,先说一下原理,3D标签云就是做一个球面,然后再球面上取均匀分布的点,把点坐标赋给标签,再根据抽象出来的Z轴大小来改变标签的字体

  • Java方法重载Overload原理及使用解析

    这篇文章主要介绍了Java方法重载Overload原理及使用解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 为什么要用方法重载: 对于功能类似的方法来说,因为参数列表不一样,如果定义不同名称的方法,太麻烦且难以记忆. 为了解决这个问题,引入方法的重载. 重载的定义: 多个方法的名称一样,但参数列表不一样. 不使用方法重载 定义三个功能类似的方法 public class TestOverload { public static int su

  • Android开发之Kotlin委托的原理与使用详解

    目录 前言 一.接口/类委托 二.属性委托 三.延迟委托 四.观察者委托 五.Map委托 总结 前言 在设计模式中,委托模式(Delegate Pattern)与代理模式都是我们常用的设计模式(Proxy Pattern),两者非常的相似,又有细小的区分. 委托模式中,委托对象和被委托对象都是同一类型的对象,委托对象将任务委托给被委托对象来完成.委托模式可以用于实现事件监听器.回调函数等功能. 代理模式中,代理对象与被代理对象是两种不同的对象,代理对象代表被代理对象的功能,代理对象可以控制客户对

  • java阻塞队列实现原理及实例解析

    这篇文章主要介绍了java阻塞队列实现原理及实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 阻塞队列与普通队列的不同在于.当队列是空的时候,从队列中获取元素的操作将会被阻塞,或者当队列满时,往队列里面添加元素将会被阻塞.试图从空的阻塞队列中获取元素的线程将会被阻塞,直到其他的线程往空的队列插入新的元素.同样,试图往已满的阻塞队列中添加新元素的线程同样也会被阻塞,直到其他的线程使队列重新变得空闲起来,如从队列中移除一个或者多个元素,或者完

随机推荐