多线程机制探索<起名转>

CAB是1种文件压缩格式,把OCX文件(ActiveX插件)压缩成CAB文件是为着公布它,
那种揭橥办法给人感到“浏览器会自动安装该插件”,很多爱人青睐于此。
时下风靡很多压缩工具,但WINRARubicon那类工具生成的CAB文件浏览器无法辨识。
由此试用,发现用Windows系统文件CABASportageC来创设CAB是最简易可行的章程,
上边以打字与印刷控件Lodop为例,一步步打听创造CAB文件和陈设发布的经过:

 感觉那些写的科学,记下来避防不时之需。

 

 

第一步、判断操作系统是还是不是有CABA奇骏C文件     判断途径如下:
操作系统 起先->运转…->键入命令cmd->键入命令 CABALANDC
若突显一大段版本注解和下令选项的英文表达,表示系统设有CABA逍客C,如若不设有能够从网上搜3个

注:本文中冒出的代码均在.net Framework 本田UR-VC叁环境中运维经过 

或者从此地下载,下载解压前置放操作系统目录下。

  一.二10三十二线程的概念 

 

  Windows是3个多职分的种类,假使您选择的是windows
贰仟会同以上版本,你可以通过职分管理器查看当前系统运转的次第和经过。什么是进度呢?当3个先后起先运维时,它正是1个历程,进度所指蕴含运行中的程序和程序所利用到的内部存款和储蓄器和系统财富。而三个进度又是由三个线程所组成的,线程是程序中的一个执行流,各类线程都有协调的专有寄存器(栈指针、程序计数器等),但代码区是共享的,即差异的线程能够进行同一的函数。二十三多线程是指程序中隐含八个执行流,即在1个先后中能够而且运转四个例外的线程来施行不一的职分,也正是说允许单个程序创设四个并行执行的线程来成功各自的天职。浏览器正是叁个很好的多线程的例证,在浏览器中你可以在下载JAVA小应用程序或图象的还要滚动页面,在造访新页面时,播放动画和声音,打字与印刷文件等。 

第二步、准备ocx文件     把打字与印刷控件Lodop的实体ocx文件复制到C:\test目录下,并起名称为Lodop.ocx
注:安装过Lodop的机械中,ocx文件存放在C:\WINDOWS\system32\CAOSOFT_WEB_PRINT_lodop.ocx

  拾②线程的裨益在于能够增进CPU的利用率——任何三个程序员都不希望自个儿的顺序很多时候有空可干,在102线程程序中,贰个线程必须等待的时候,CPU能够运作其它的线程而不是伺机,那样就大大进步了程序的功能。 

或Program
Files\MountTaiSoftware\Lodop下。
或者点此下载

  可是我们也亟须认识到线程自身只怕影响系统品质的不利方面,以科学利用线程:

 

线程也是程序,所以线程须要占用内部存款和储蓄器,线程越来越多占用内部存款和储蓄器也越来越多

第三步、制作CAB文件    在第2步的cmd窗口中,键入如下命令:
CABARC -s 6144 N c:\test\lodop.cab
c:\test\lodop.ocx
该命令执行达成后,在C:\test目录下会看到新生成的Lodop.cab文件。
在那行命令中,CABA奥德赛C之后有七个参数值,参数之间用空格分隔,
里面 -s 6144 俩参数表示生成的CAB文件从此能够展开代码签名,
假诺未来不想签名,该参数能够不加,代码签名是个很纠结的题材,今后细说。

四线程需求协调和管理,所以必要CPU时间跟踪线程

 

线程之间对共享资源的走访会互相影响,必须消除竞用共享能源的题目

以上那步还有个不难方法,就是把该命令行字串用记事本复制到三个bat文件中,
比如起名CreateLodopCAB.bat,需求生成CAB文件时双击该bat文件就可。
该措施切合平日公布同一产品的分裂版本。

线程太多会招致控制太复杂,最后恐怕造成众多Bug

 

  基于以上认识,大家得以二个比喻来深化领悟。借使有2个公司,公司里有诸多同心协力的老干,那么大家得以认为这一个符合规律运营的同盟社正是一个经过,而集团里的干部便是线程。2个商厦至少得有二个人士吧,同理,一个进度至少含有3个线程。在同盟社里,你能够1个老干干全数的事,但是效用很显明是高不起来的,一人的商户也不容许做大;三个程序中也能够只用1个线程去做事,事实上,一些过时的语言如fortune,basic都以如此,然则象一个人的小卖部1如既往,功能十分的低,假使做大程序,功用更低——事实上现在大概从不单线程的商业软件。公司的老干越来越多,总主管就得发更加多的工资给他们,还得花费多量生气去管理他们,协调他们中间的争论和好处;程序也是那样,线程越来越多损耗的财富也更多,要求CPU时间去跟踪线程,还得化解诸如死锁,同步等难点。总而言之,即便您不想你的卖家被叫做“皮包公司”,你就得多多少个职员和工人;假设您不想让您的程序显得稚嫩,就在你的程序里引进十二线程吧! 

第五步、CAB文件的运用办法1     插件用CAB发表的最简便方法是页面object对象的codebase直接指向该CAB文件,
把上面那段完整的超文本复制到一个html文件中,起名称叫demo.html:

  本文将对C#编制程序中的二十多线程机制举办追究,通过有个别实例化解对线程的决定,八线程间通讯等难题。为了节约创设GUI那么些繁琐的步子,更分明地逼近线程的大茂山真面目,上边全数的顺序都以控制台程序,程序最终的Console.ReadLine()是为着使程序中途停下来,以便看驾驭执行进度中的输出。 

<html>
<H1> Lodop CAB File Test Page </H1><p>
<object
   classid=”clsid:2105C259-1E0C-4534-8141-A753534CB4CA”
   codebase=”lodop.cab”
   width=200
   height=100
>
</object>
</html>

  好了,废话少说,让大家来体验一下二十八线程的C#吧! 

把文件demo.html和第贰步生成的lodop.cab放到同三个web目录下就ok了。
以上这段代码应该很好掌握,其中lodop.cab在此之前还足以涵盖web路径,
相当于说插件能够单独布署,通过路径指向具体的陈设地方。
当今您把浏览器的U中华VL通过web打开demo.html文件,测试看看是否“自动安装”。

  贰.操纵1个线程 

 

  任何程序在实践时,至少有2个主线程,上面那段小程序能够给读者1个直观的记念:

第4步、CAB文件的运用方法2     页面还是能够通过2个inf文件来发布插件,以便安插多个ocx,此时超文本变成如下:

//SystemThread.cs

<html>
<H1> Lodop Test Page </H1><p>
<object
   classid=”clsid:2105C259-1E0C-4534-8141-A753534CB4CA”
   codebase=”lodop.inf”
   width=0
   height=0
>
</object>
</html>

using System;

其间lodop.inf是txt格式,内容如下:

using System.Threading; 

[Add.Code]
lodop.ocx=lodop.ocx

namespace ThreadTest

 

{

[lodop.ocx]
file=lodop.cab
clsid={2105C259-1E0C-4534-8141-A753534CB4CA}
RegisterServer=yes
FileVersion=6,0,5,2

  class RunIt

这一个代码还算好精晓,不用累述。

  {

    以上简单几步就兑现了网页插件的“自动安装”安插,但遗憾的是,
在实践中那种设置卓殊“不活动”,因浏览器阻断而设置失利的概率极大。
本着那种情况,无论是闻名的Flash依旧Lodop,都提供了exe格式的安装程序,

    [STAThread]

让用户下载后实施安装。当然2者组合起来安排,则是不错的精选!

    static void Main(string[] args)

来源大学生恋爱网

    {

      Thread.CurrentThread.Name=”System
Thread”;//给当下线程起名叫”System Thread”

Console.WriteLine(Thread.CurrentThread.Name+”‘Status:”+Thread.CurrentThread.ThreadState);

      Console.ReadLine();

    }

  }

}

  编译执行后您看来了怎么着?是的,程序将发出如下输出: 

  System Thread’s Status:Running 

  在此间,大家经过Thread类的静态属性CurrentThread获取了眼前举办的线程,对其Name属性赋值“System
Thread”,最终还输出了它的当下境况(ThreadState)。所谓静态属性,正是其壹类具有指标所国有的天性,不管您成立了不怎么个那些类的实例,不过类的静态属性在内存中唯有3个。很简单理解CurrentThread为什么是静态的——即使有几个线程同时存在,不过在某三个时时,CPU只可以执行当中三个。 

  就好像上边程序所示范的,大家透过Thread类来创设和决定线程。注意到程序的底部,大家运用了如下命名空间:

  using System;

  using System.Threading;

  在.net framework class
library中,全部与多线程机制应用相关的类都以位于System.Threading命名空间中的。在那之中提供Thread类用于创制线程,ThreadPool类用于管理线程池等等,其余还提供化解了线程执行安排,死锁,线程间通信等实际难点的建制。借使你想在你的应用程序中动用102线程,就必须包涵那些类。Thread类有多少个关键的点子,描述如下:

Start():运转线程

Sleep(int):静态方法,暂停当前线程钦点的飞秒数

Abort():平常使用该方法来终止三个线程

Suspend():该格局并不停歇未到位的线程,它唯有挂起线程,未来还可过来。

Resume():恢复生机被Suspend()方法挂起的线程的推行

1

上边大家就入手来创立1个线程,使用Thread类创造线程时,只需提供线程入口即可。线程入口使程序知道该让那个线程干什么事,在C#中,线程入口是经过ThreadStart代理(delegate)来提供的,你能够把ThreadStart驾驭为一个函数指针,指向线程要推行的函数,当调用Thread.Start()方法后,线程就从头实践ThreadStart所代表或许说指向的函数。 

  打开你的VS.net,新建3个控制台应用程序(Console
Application),下边那个代码将让你体味到完全控制3个线程的无穷乐趣!

  //ThreadTest.cs 

  using System;

  using System.Threading; 

  namespace ThreadTest

  {

  public class Alpha

    {

      public void Beta()

      {

        while (true)

        {

          Console.WriteLine(“Alpha.Beta is running in its own
thread.”);

        }

      }

    }; 

    public class Simple

    {

      public static int Main()

      {

        Console.WriteLine(“Thread Start/Stop/Join Sample”);

        Alpha oAlpha = new Alpha();

        file://这里开创一个线程,使之执行Alpha类的Beta()方法

        Thread oThread = new Thread(new
ThreadStart(oAlpha.Beta));

        oThread.Start();

        while (!oThread.IsAlive);

          Thread.Sleep(1);

        oThread.Abort();

        oThread.Join();

        Console.WriteLine();

        Console.WriteLine(“Alpha.Beta has finished”); 

        try 

        {

          Console.WriteLine(“Try to restart the Alpha.Beta
thread”);

          oThread.Start();

        }

        catch (ThreadStateException) 

        {

          Console.Write(“ThreadStateException trying to
restart Alpha.Beta. “);

          Console.WriteLine(“Expected since aborted threads
cannot be restarted.”);

          Console.ReadLine();

        }

        return 0;

      }

    }

  }

  这段程序包涵多少个类Alpha和Simple,在创造线程oThread时大家用指向Alpha.Beta()方法的开端化了ThreadStart代理(delegate)对象,当大家成立的线程oThread调用oThread.Start()方法运营时,实际上程序运维的是Alpha.Beta()方法:

  Alpha oAlpha = new Alpha();

  Thread oThread = new Thread(new ThreadStart(oAlpha.Beta));

  oThread.Start();

  然后在Main()函数的while循环中,大家采纳静态方法Thread.Sleep()让主线程停了1ms,那段日子CPU转向执行线程oThread。然后大家试图用Thread.Abort()方法终止线程oThread,注意后边的oThread.Join(),Thread.Join()方法使主线程等待,直到oThread线程甘休。你能够给Thread.Join()方法钦点3个int型的参数作为等待的最长日子。之后,大家试图用Thread.Start()方法重复开动线程oThread,然而显明Abort()方法带来的后果是不行恢复生机的平息线程,所以最后程序会抛出ThreadStateException极度。 

  

在此地大家要小心的是其余线程都是专属于Main()函数所在的线程的,Main()函数是C#程序的进口,初始线程能够称之为主线程,借使全体的前台线程都甘休了,那么主线程能够告一段落,而拥有的后台线程都将职分甘休。而全部的线程就算在微观上是串行执行的,可是在微观上您一点1滴能够认为它们在并行执行。 

  读者必定注意到了Thread.ThreadState那特特性,那特本性代表了线程运维时景况,在分化的状态下有不一致的值,于是我们有时候能够经过对该值的论断来统一筹划程序流程。ThreadState在各类情形下的也许取值如下:

Aborted:线程已终止

AbortRequested:线程的Thread.Abort()方法已被调用,不过线程还未结束

Background:线程在后台执行,与天性Thread.IsBackground有关

Running:线程正在健康运作

Stopped:线程已经被结束

StopRequested:线程正在被需要终止

Suspended:线程已经被挂起(此情景下,能够通过调用Resume()方法重国民党的新生活运动行)

SuspendRequested:线程正在需要被挂起,但是今后得及响应

Unstarted:未调用Thread.Start()开头线程的运作

WaitSleepJoin:线程因为调用了Wait(),Sleep()或Join()等方式处于封锁状态

  上面提到了Background状态表示该线程在后台运转,那么后台运维的线程有怎么着更加的地点吗?其实后台线程眼前台线程只有三个组别,那正是后台线程无妨碍程序的停下。一旦叁个历程具有的前台线程都终止后,CL兰德昂科威(通用语言运营环境)将通过调用任意1个存活中的后台进度的Abort()方法来彻底结束进度。 

  当线程之间争夺CPU时间时,CPU遵照是线程的优先级给予劳动的。在C#应用程序中,用户能够设定四个不等的优先级,由高到低分别是Highest,AboveNormal,诺玛l,Below诺玛l,Lowest,在成立线程时假使不钦命优先级,那么系统暗中同意为ThreadPriority.Normal。给三个线程钦赐优先级

,我们得以行使如下代码:

  //设定优先级为最低

  myThread.Priority=ThreadPriority.Lowest;

  通过设定线程的优先级,大家可以配备部分相对首要的线程优先实施,例如对用户的响应等等。 

  未来我们对什么样创制和决定3个线程已经有了3个发轫的领会,上面我们将深远研讨线程达成中比较典型的的难题,并且商讨其化解格局。 

  三.线程的联合署名和报纸发表——生产者和买主 

  假诺那样一种意况,七个线程同时保险七个队列,要是1个线程对队列中添日币素,而别的3个线程从队列中取用元素,那么我们称添法郎素的线程为劳动者,称取用成分的线程为买主。生产者与买主问题看起来不会细小略,可是却是10贰线程应用中3个务必消除的题材,它涉及到线程之间的联合署名和通信难点。 

  前边说过,种种线程都有友好的能源,可是代码区是共享的,即每一个线程都足以实施同1的函数。但是102线程环境下,恐怕带来的标题正是多少个线程同时施行一个函数,导致数据的糊涂,发生不可预料的结果,因而大家无法不防止那种景色的爆发。C#提供了三个最重要字lock,它可以把壹段代码定义为互斥段(critical
section),互斥段在贰个时刻内只同意2个线程进入实践,而其它线程必须等待。在C#中,关键字lock定义如下:

  lock(expression) statement_block

expression代表你期望跟踪的对象,平常是目的引用。一般地,假使你想爱戴3个类的实例,你能够选取this;假使你希望保养一个静态变量(如互斥代码段在2个静态方法内部),壹般选拔类名就能够了。而statement_block便是排斥段的代码,这段代码在三个随时内只也许被1个线程执行。 

  上边是二个使用lock关键字的超人例子,小编将在诠释里向大家表达lock关键字的用法和用途:

  //lock.cs

  using System;

  using System.Threading; 

  internal class Account 

  {

  int balance;

  Random r = new Random();

  internal Account(int initial) 

  {

    balance = initial;

  } 

  internal int Withdraw(int amount) 

  {

    if (balance < 0) 

    {

    file://借使balance小于0则抛出尤其

    throw new Exception(“Negative Balance”);

    }

    //上面包车型地铁代码保险在如今线程修改balance的值达成以前

    //不会有任何线程也举办那段代码来修改balance的值

    //因而,balance的值是不容许小于0的

    lock (this)

    {

    Console.WriteLine(“Current Thread:”+Thread.CurrentThread.Name);

    file://要是没有lock关键字的掩护,那么恐怕在履行完if的基准判断之后

    file://其余3个线程却执行了balance=balance-amount修改了balance的值

    file://而那一个修改对这些线程是不可知的,所以恐怕导致那时if的标准化已经不创建了

    file://可是,这几个线程却继续执行balance=balance-amount,所以造成balance恐怕小于0

    if (balance >= amount) 

    {

      Thread.Sleep(5);

      balance = balance – amount;

      return amount;

    } 

    else 

    {

      return 0; // transaction rejected

    }

    }

  }

  internal void DoTransactions() 

  {

    for (int i = 0; i < 100; i++) 

    Withdraw(r.Next(-50, 100));

  }

  } 

  internal class Test 

  {

  static internal Thread[] threads = new Thread[10];

  public static void Main() 

  {

    Account acc = new Account (0);

    for (int i = 0; i < 10; i++) 

    {

    Thread t = new Thread(new ThreadStart(acc.DoTransactions));

    threads[i] = t;

    }

    for (int i = 0; i < 10; i++) 

    threads[i].Name=i.ToString();

    for (int i = 0; i < 10; i++) 

    threads[i].Start();

    Console.ReadLine();

  }

  }

  而102线程公用三个对象时,也会并发和公用代码类似的标题,那种难点就不应该选择lock关键字了,那里必要用到System.Threading中的三个类Monitor,大家得以称为监视器,Monitor提供了使线程共享财富的方案。 

  Monitor类能够锁定一个对象,二个线程唯有收获这把锁才得以对该指标开展操作。对象锁机制保险了在大概滋生混乱的场地下二个时时惟有1个线程能够访问这么些目的。Monitor必须和贰个切实的对象相关联,然则出于它是一个静态的类,所以不可能动用它来定义对象,而且它的兼具办法都以静态的,不能够采取对象来引用。下边代码表明了使用Monitor锁定三个指标的意况:

  ……

  Queue oQueue=new Queue();

  ……

  Monitor.Enter(oQueue);

  ……//未来oQueue对象只可以被当下线程操纵了

  Monitor.Exit(oQueue);//释放锁

  如上所示,当三个线程调用Monitor.Enter()方法锁定1个对象时,这几个指标就归它具备了,其它线程想要访问那一个目的,唯有静观其变它利用Monitor.Exit()方法释放锁。为了确认保证线程最后都能释放锁,你能够把Monitor.Exit()方法写在try-catch-finally结构中的finally代码块里。对于其他1个被Monitor锁定的靶子,内部存储器中都保存着与它相关的部分新闻,其1是明天颇具锁的线程的引用,其贰是多少个预备队列,队列中保留了一度准备好收获锁的线程,其三是3个等候队列,队列中保存着眼下正值班守护候那几个目的情状改变的体系的引用。当全部对象锁的线程准备释放锁时,它采纳Monitor.Pulse()方法布告等待队列中的第3个线程,于是该线程被更换来预备队列中,当指标锁被放飞时,在备选队列中的线程能够即时赢得对象锁。 

  下边是三个出示什么选用lock关键字和Monitor类来达成线程的一块和报导的事例,也是1个第一名的劳动者与买主难题。这么些例程中,生产者线程和消费者线程是轮岗举行的,生产者写入叁个数,消费者即时读取并且出示,作者将在诠释中介绍该程序的精要所在。用到的系统命名空间如下:

  using System;

  using System.Threading;

率先,大家定义三个被操作的靶子的类Cell,在那几个类里,有五个办法:ReadFromCell()和WriteToCell。消费者线程将调用ReadFromCell()读取cellContents的剧情还要展现出来,生产者进度将调用WriteToCell()方法向cellContents写入数据。

  public class Cell

  {

  int cellContents; // Cell对象里边的始末

  bool readerFlag = false; //
状态标志,为true时得以读取,为false则正在写入

  public int ReadFromCell( )

  {

    lock(this) // Lock关键字确定保障了什么样,请大家看眼下对lock的牵线

    {

    if (!readerFlag)//即使今后不得读取

    { 

      try

      {

      file://等待WriteToCell方法中调用Monitor.Pulse()方法

      Monitor.Wait(this);

      }

      catch (SynchronizationLockException e)

      {

      Console.WriteLine(e);

      }

      catch (ThreadInterruptedException e)

      {

      Console.WriteLine(e);

      }

    }

    Console.WriteLine(“Consume: {0}”,cellContents);

    readerFlag = false;
file://重置readerFlag标志,表示消费行为已经形成

    Monitor.Pulse(this);
file://布告WriteToCell()方法(该办法在其余1个线程中施行,等待中)

    }

    return cellContents;

  }

  public void WriteToCell(int n)

  {

    lock(this)

    {

    if (readerFlag)

    {

      try

      {

      Monitor.Wait(this);

      }

      catch (SynchronizationLockException e)

      {

      file://当同步方法(指Monitor类除Enter之外的格局)在非同步的代码区被调用

      Console.WriteLine(e);

      }

      catch (ThreadInterruptedException e)

      {

      file://当线程在守候情形的时候抛锚 

      Console.WriteLine(e);

      }

    }

    cellContents = n;

    Console.WriteLine(“Produce: {0}”,cellContents);

    readerFlag = true; 

    Monitor.Pulse(this);
file://布告此外3个线程中正在等待的ReadFromCell()方法

    }

  }

  }

  上边定义生产者CellProd和顾客类CellCons,它们都只有五个艺术ThreadRun(),以便在Main()函数中提必要线程的ThreadStart代理对象,作为线程的入口。

  public class CellProd

  {

  Cell cell; // 被操作的Cell对象

  int quantity = 一; // 生产者生产次数,开头化为一 

  public CellProd(Cell box, int request)

  {

    //构造函数

    cell = box; 

    quantity = request; 

  }

  public void ThreadRun( )

  {

    for(int looper=1; looper<=quantity; looper++)

    cell.WriteToCell(looper); file://生产者向操作对象写入音信

  }

  } 

  public class CellCons

  {

  Cell cell; 

  int quantity = 1; 

  public CellCons(Cell box, int request)

  {

    cell = box; 

    quantity = request; 

  }

  public void ThreadRun( )

  {

    int valReturned;

    for(int looper=1; looper<=quantity; looper++)

    valReturned=cell.ReadFromCell( );//消费者从操作对象中读取音讯

  }

  }

下一场在底下这几个类Monitor萨姆ple的Main()函数中大家要做的正是创办七个线程分别作为劳动者和顾客,使用CellProd.ThreadRun()方法和CellCons.ThreadRun()方法对同二个Cell对象开始展览操作。

  public class MonitorSample

  {

  public static void Main(String[] args)

  {

    int result = 0;
file://三个标志位,假使是0表示程序尚未出错,借使是一标志有错误发生

    Cell cell = new Cell( ); 

    //上边采纳cell开始化CellProd和CellCons四个类,生产和消费次数均为23回

    CellProd prod = new CellProd(cell, 20); 

    CellCons cons = new CellCons(cell, 20); 

    Thread producer = new Thread(new ThreadStart(prod.ThreadRun));

    Thread consumer = new Thread(new ThreadStart(cons.ThreadRun));

    //生产者线程和买主线程都曾经被创制,可是从未从头执行 

    try

    {

    producer.Start( );

    consumer.Start( ); 

    producer.Join( ); 

    consumer.Join( );

    Console.ReadLine();

    }

    catch (ThreadStateException e)

    {

    file://当线程因为所处状态的原委而无法履行被呼吁的操作

    Console.WriteLine(e); 

    result = 1; 

    }

    catch (ThreadInterruptedException e)

    {

    file://当线程在伺机状态的时候抛锚

    Console.WriteLine(e); 

    result = 1; 

    }

    //固然Main()函数未有再次来到值,但下边那条语句能够向父进度重回执行结果

    Environment.ExitCode = result;

  }

  }

世家可以看看,在上头的例程中,同步是因此等待Monitor.Pulse()来形成的。首先生产者生产了二个值,而同一时刻消费者处于等候状态,直到收到生产者的“脉冲(Pulse)”公告它生产1度做到,此后消费者进入消费景况,而生产者初步等候顾客完成操作后将调用Monitor.Pulese()发出的“脉冲”。它的实践结果很粗大略:

  Produce: 1

  Consume: 1

  Produce: 2

  Consume: 2

  Produce: 3

  Consume: 3

  …

  …

  Produce: 20

  Consume: 20

  事实上,这么些大致的例子已经帮助大家消除了二十八线程应用程序中只怕出现的大题材,只要精通了消除线程间争论的着力措施,很简单把它应用到相比较复杂的主次中去。 

  肆、线程池和定时器——拾2线程的机关管理 

  在多线程的程序中,平时会并发三种境况。1种景况下,应用程序中的线程把半数以上的时间开支在等候意况,等待有个别事件时有产生,然后才能加之响应;而除此以外一种情景则是线程常常都处于休眠状态,只是周期性地被唤醒。在.net
framework里边,我们利用ThreadPool来对付第2种意况,使用Timer来对付第一种景况。

  ThreadPool类提供2个由系统保险的线程池——能够看做三个线程的器皿,该容器必要Windows
3000之上版本的系列帮助,因为内部1些方法调用了唯有高版本的Windows才有的API函数。你能够应用ThreadPool.QueueUserWorkItem()方法将线程安置在线程池里,该措施的原型如下:

  //将叁个线程放进线程池,该线程的Start()方法将调用WaitCallback代理对象表示的函数

  public static bool QueueUserWorkItem(WaitCallback);

  //重载的办法如下,参数object将传递给WaitCallback所代表的点子

  public static bool QueueUserWorkItem(WaitCallback, object);

  要专注的是,ThreadPool类也是1个静态类,你不可能也不供给生成它的靶子,而且只要接纳该方法在线程池中添加了二个项目,那么该类型将是尚未章程撤除的。在此间你无需协调树立线程,只需把您要做的干活写成函数,然后作为参数字传送递给ThreadPool.QueueUserWorkItem()方法就行了,传递的章程正是注重WaitCallback代理对象,而线程的树立、管理、运维等等工作都以由系统活动完毕的,你不用思量那一个复杂的细节难点,线程池的帮助和益处也就在那里反映出来了,就象是你是商店COO——只须要配置工作,而不用亲自入手。

上面包车型大巴例程演示了ThreadPool的用法。首先程序创设了多个马努alReset伊夫nt对象,该对象就好像1个复信号灯,能够选取它的复信号来打招呼其余线程,本例中当线程池中享有线程工作都成功之后,马努alReset伊夫nt的指标将棉被服装置为有复信号,从而公告主线程继续运维。它有几个不能缺少的情势:Reset(),Set(),WaitOne()。初阶化该对象时,用户能够钦定其暗许的场合(有复信号/无功率信号),在初阶化以往,该对象将保持原来的动静不变直到它的Reset()恐怕Set()方法被调用,Reset()方法将其安装为无功率信号状态,Set()方法将其设置为有功率信号状态。WaitOne()方法使近日线程挂起直至马努alReset伊芙nt对象处于有信号状态,此时该线程将被激活。然后,程序将向线程池中添加工作项,这个以函数方式提供的做事项被系统用来开端化自动建立的线程。当全体的线程都运营完了后来,马努alReset伊芙nt.Set()方法被调用,因为调用了马努alReset伊夫nt.WaitOne()方法而处于等待意况的主线程将接收到这几个随机信号,于是它随着往下实施,完毕前面包车型地铁工作。

  using System;

  using System.Collections;

  using System.Threading;

  //那是用来保存新闻的数据结构,将用作参数被传送

  public class SomeState

  {

  public int Cookie;

  public SomeState(int iCookie)

  {

    Cookie = iCookie;

  }

  }

  public class Alpha

  {

  public Hashtable HashCount;

  public ManualResetEvent eventX;

  public static int iCount = 0;

  public static int iMaxCount = 0;

  public Alpha(int MaxCount) 

  {

    HashCount = new Hashtable(MaxCount);

    iMaxCount = MaxCount;

  }

  file://线程池里的线程将调用Beta()方法

  public void Beta(Object state)

  {

    //输出当前线程的hash编码值和Cookie的值

    Console.WriteLine(” {0} {1} :”,
Thread.CurrentThread.GetHashCode(),

    ((SomeState)state).Cookie);

    Console.WriteLine(“HashCount.Count=={0},
Thread.CurrentThread.GetHashCode()=={1}”, HashCount.Count,
Thread.CurrentThread.GetHashCode());

    lock (HashCount) 

    {

    file://假设当前的Hash表中从不当前线程的Hash值,则添加之

    if (!HashCount.ContainsKey(Thread.CurrentThread.GetHashCode()))

      HashCount.Add (Thread.CurrentThread.GetHashCode(), 0);

    HashCount[Thread.CurrentThread.GetHashCode()] = 

((int)HashCount[Thread.CurrentThread.GetHashCode()])+1;

    }

    int iX = 2000;

    Thread.Sleep(iX);

    //Interlocked.Increment()操作是三个原子操作,具体请看下边表明

    Interlocked.Increment(ref iCount);

    if (iCount == iMaxCount)

    {

    Console.WriteLine();

    Console.WriteLine(“Setting eventX “);

    eventX.Set();

    }

  }

  }

  public class SimplePool

  {

  public static int Main(string[] args)

  {

    Console.WriteLine(“Thread Pool Sample:”);

    bool W2K = false;

    int 马克斯Count = 10;//允许线程池中运作最多十二个线程

    //新建ManualReset伊芙nt对象并且开始化为无时限信号状态

    ManualResetEvent eventX = new ManualResetEvent(false);

    Console.WriteLine(“Queuing {0} items to Thread Pool”, MaxCount);

    Alpha oAlpha = new Alpha(马克斯Count); file://创设工作项

    //注意开首化oAlpha对象的eventX属性

    oAlpha.eventX = eventX;

    Console.WriteLine(“Queue to Thread Pool 0”);

    try

    {

    file://将工作项装入线程池 

    file://那里要用到Windows
两千之上版本才有的API,所以大概出现NotSupportException非常

    ThreadPool.QueueUserWorkItem(new WaitCallback(oAlpha.Beta),

    new SomeState(0));

    W2K = true;

    }

    catch (NotSupportedException)

    {

    Console.WriteLine(“These API’s may fail when called on a
non-Windows 2000 system.”);

    W2K = false;

    }

    if (W2K)//要是当前系统援救ThreadPool的方法.

    {

    for (int iItem=1;iItem < MaxCount;iItem++)

    {

      //插入队列成分

      Console.WriteLine(“Queue to Thread Pool {0}”, iItem);

      ThreadPool.QueueUserWorkItem(new
WaitCallback(oAlpha.Beta),new SomeState(iItem));

    }

    Console.WriteLine(“Waiting for Thread Pool to drain”);

    file://等待事件的做到,即线程调用马努alReset伊芙nt.Set()方法

    eventX.WaitOne(Timeout.Infinite,true);

    file://WaitOne()方法使调用它的线程等待直到eventX.Set()方法被调用

    Console.WriteLine(“Thread Pool has been drained (Event fired)”);

    Console.WriteLine();

    Console.WriteLine(“Load across threads”);

    foreach(object o in oAlpha.HashCount.Keys)

    Console.WriteLine(“{0} {1}”, o, oAlpha.HashCount[o]);

    }

    Console.ReadLine();

    return 0;

  }

  }

  程序中有个别小地点应该引起大家的令人瞩目。SomeState类是1个保存新闻的数据结构,在上边的次序中,它当作参数被传送给每一个线程,你很简单就能分晓这几个,因为你必要把壹部分得力的音讯打包起来提须求线程,而那种措施是不行政管理用的。程序出现的Inter洛克d类也是专为二十八线程程序而留存的,它提供了一些实惠的原子操作,所谓原子操作正是在八线程程序中,假使那一个线程调用那个操作修改一个变量,那么其它线程就不可能修改那么些变量了,那跟lock关键字在真相上是壹致的。

  大家理应彻底地分析上面包车型客车顺序,把握住线程池的真相,驾驭它存在的意思是如何,那样大家才能百发百中地动用它。下边是该程序的出口结果:

  Thread Pool Sample:

  Queuing 10 items to Thread Pool

  Queue to Thread Pool 0

  Queue to Thread Pool 1

  …

  …

  Queue to Thread Pool 9

  Waiting for Thread Pool to drain

  98 0 :

  HashCount.Count==0, Thread.CurrentThread.GetHashCode()==98

  100 1 :

  HashCount.Count==1, Thread.CurrentThread.GetHashCode()==100

  98 2 :

  …

  …

  Setting eventX

  Thread Pool has been drained (Event fired) 

  Load across threads

  101 2

  100 3

  98 4

  102 1

  与ThreadPool类差别,Timer类的法力是设置二个定时器,定时执行用户内定的函数,而这些函数的传递是靠其它3个代理对象TimerCallback,它必须在创立提姆er对象时就钦定,并且不可能改变。定时器运维后,系统将自行建立多个新的线程,并且在那些线程里进行用户钦定的函数。上面包车型地铁讲话开头化了1个提姆er对象:

  Timer timer = new Timer(timerDelegate, s,1000, 1000);

  第二个参数钦定了提姆erCallback代理对象;第一个参数的意义跟上面提到的WaitCallback代理对象的同样,作为2个传递数据的靶子传递给要调用的格局;第多个参数是延迟时间——计时开头的每一日距未来的时刻,单位是纳秒;第多少个参数是定时器的日子间隔——计时起首之后,每隔这么长的壹段时间,TimerCallback所表示的主意将被调用3遍,单位也是纳秒。这句话的意趣就是将定时器的延迟时间和时间距离都设为一分钟。

  定时器的装置是足以转移的,只要调用Timer.Change()方法,那是叁个参数类型重载的主意,一般采取的原型如下:

   public bool Change(long, long);

  上面那段代码将前方设置的定时器修改了弹指间:

   timer.Change(10000,2000);

  很显眼,定时器timer的时日间隔被再次设置为二秒,结束计时10秒后生效。

  上边这段程序演示了Timer类的用法。

  using System;

  using System.Threading; 

  class TimerExampleState 

  {

  public int counter = 0;

  public Timer tmr;

  }

  class App 

  {

  public static void Main()

  {

    TimerExampleState s = new TimerExampleState();

    //成立代理对象TimerCallback,该代理将被定时调用

    TimerCallback timerDelegate = new TimerCallback(CheckStatus);

    //创设三个小时间隔为一s的定时器

    Timer timer = new Timer(timerDelegate, s,1000, 1000);

    s.tmr = timer;

    //主线程停下来等待提姆er对象的告1段落

    while(s.tmr != null)

    Thread.Sleep(0);

    Console.WriteLine(“Timer example done.”);

    Console.ReadLine();

  }

  file://上面是被定时调用的不二等秘书诀

  static void CheckStatus(Object state)

  {

    TimerExampleState s =(TimerExampleState)state;

    s.counter++;

    Console.WriteLine(“{0} Checking Status
{1}.”,DateTime.Now.TimeOfDay, s.counter);

    if(s.counter == 5)

    {

    file://使用Change方法改变了光阴世隔

    (s.tmr).Change(10000,2000);

    Console.WriteLine(“changed…”);

    }

    if(s.counter == 10)

    {

    Console.WriteLine(“disposing of timer…”);

    s.tmr.Dispose();

    s.tmr = null;

    }

  }

  }

  程序首先创造了1个定时器,它将在开创1秒以后开头每隔壹秒调用2次CheckStatus()方法,当调用六次之后,在CheckStatus()方法中修改了光阴距离为二秒,并且钦赐在十秒后再也开首。当计数达到1肆遍,调用提姆er.Dispose()方法删除了timer对象,主线程于是跳出循环,终止程序。程序执行的结果如下:

  上边便是对ThreadPool和Timer八个类的简便介绍,足够利用系统提供的效力,能够为大家省去过多光阴和精力——尤其是对很简单出错的多线程程序。同时我们也足以看到.net
Framework强大的放权对象,那个将对大家的编制程序带来可观的便宜。

、互斥对象——更灵敏的壹块儿形式

  有时候你会认为上边介绍的章程好像不够用,对,大家化解了代码和能源的一起难点,消除了二十四线程自动化管理和定时触发的题材,不过怎么支配四个线程相互之间的关系呢?例如小编要到餐厅用餐,在吃饭在此以前本身先得拭目以待厨子把饭菜做好,之后小编起来进食,吃完自家还得付款,付款格局得以是现金,也足以是信用卡,付款之后笔者才能离开。分析一下以此进度,作者吃饭能够看做是主线程,大厨做饭又是三个线程,服务员用信用卡收款和收现金可以视作其余多个线程,大家能够很清楚地看到里面包车型客车关联——作者吃饭必须等待大厨做饭,然后等待五个收取金钱线程之中任意三个的实现,然后小编吃饭这些线程能够进行离开这些手续,于是作者吃饭才算长逝了。事实上,现实中有着比那更复杂的维系,大家怎么样才能很好地操纵它们而不发出争持和再度呢?

  那种处境下,大家供给选拔互斥对象,即System.Threading命名空间中的Mutex类。我们自然坐过出租车吗,事实上大家得以把Mutex看作多个出租车,那么游客就是线程了,游客首先得等车,然后上车,最终下车,当1个司乘职员在车上时,其余旅客就唯有等她下车之后才得以上车。而线程与Mutex对象的关联约等于如此,线程使用Mutex.WaitOne()方法等待Mutex对象被假释,假诺它等待的Mutex对象被假释了,它就自动拥有那么些目的,直到它调用Mutex.ReleaseMutex()方法释放这几个目的,而在此期间,别的想要获取那几个Mutex对象的线程都唯有拭目以待。

  上面那么些例子使用了Mutex对象来共同八个线程,主线程等待几个线程的利落,而那多少个线程的运转又是与三个Mutex对象相关联的。个中还用到AutoReset伊夫nt类的目的,仿佛下边提到的马努alResetEvent对象一样,大家能够把它大致地领悟为2个随机信号灯,使用AutoReset伊芙nt.Set()方法能够安装它为有能量信号状态,而利用AutoReset伊芙nt.Reset()方法把它设置为无复信号状态。这里用它的有功率信号状态来代表三个线程的完工。

  // Mutex.cs

  using System;

  using System.Threading;

  public class MutexSample

  {

  static Mutex gM1;

  static Mutex gM2;

  const int ITERS = 100;

  static AutoResetEvent Event1 = new AutoResetEvent(false);

  static AutoResetEvent Event2 = new AutoResetEvent(false);

  static AutoResetEvent Event3 = new AutoResetEvent(false);

  static AutoResetEvent Event4 = new AutoResetEvent(false);

  public static void Main(String[] args)

  {

    Console.WriteLine(“Mutex Sample …”);

    //成立3个Mutex对象,并且命名叫MyMutex

    gM1 = new Mutex(true,”MyMutex”);

    //成立一个未命名的Mutex 对象.

    gM2 = new Mutex(true);

    Console.WriteLine(” – Main Owns gM1 and gM2″);

    AutoResetEvent[] evs = new AutoResetEvent[4];

    evs[0] = 伊芙nt壹;
file://为前边的线程t1,t二,t叁,t肆定义AutoReset伊夫nt对象

    evs[1] = Event2; 

    evs[2] = Event3; 

    evs[3] = Event4; 

    MutexSample tm = new MutexSample( );

    Thread t1 = new Thread(new ThreadStart(tm.t1Start));

    Thread t2 = new Thread(new ThreadStart(tm.t2Start));

    Thread t3 = new Thread(new ThreadStart(tm.t3Start));

    Thread t4 = new Thread(new ThreadStart(tm.t4Start));

    t一.Start( );//
使用Mutex.WaitAll()方法等待三个Mutex数组中的对象全体被放飞

    t贰.Start( );// 使用Mutex.WaitOne()方法等待gM一的释放

    t三.Start( );//
使用Mutex.WaitAny()方法等待四个Mutex数组中自由三个对象被释放

    t4.Start( );// 使用Mutex.WaitOne()方法等待gM2的刑满释放解除劳教

    Thread.Sleep(2000);

    Console.WriteLine(” – Main releases gM1″);

    gM一.ReleaseMutex( ); file://线程t2,t三停止条件餍足

    Thread.Sleep(1000);

    Console.WriteLine(” – Main releases gM2″);

    gM贰.ReleaseMutex( ); file://线程t一,t4结束条件知足

    //等待全部多少个线程甘休

    WaitHandle.WaitAll(evs); 

    Console.WriteLine(“… Mutex Sample”);

    Console.ReadLine();

  }

  public void t1Start( )

  {

    Console.WriteLine(“t1Start started, Mutex.WaitAll(Mutex[])”);

    Mutex[] gMs = new Mutex[2];

    gMs[0] = gM壹;//创立一个Mutex数组作为Mutex.WaitAll()方法的参数

    gMs[1] = gM2;

    Mutex.WaitAll(gMs);//等待gM一和gM二都被释放

    Thread.Sleep(2000);

    Console.WriteLine(“t1Start finished, Mutex.WaitAll(Mutex[])
satisfied”);

    伊芙nt壹.Set( ); file://线程甘休,将伊芙nt1安装为有能量信号状态

  }

  public void t2Start( )

  {

    Console.WriteLine(“t2Start started, gM1.WaitOne( )”);

    gM1.WaitOne( );//等待gM1的释放

    Console.WriteLine(“t2Start finished, gM1.WaitOne( ) satisfied”);

    伊夫nt二.Set( );//线程截至,将伊夫nt贰设置为有实信号状态

  }

  public void t3Start( )

  {

    Console.WriteLine(“t3Start started, Mutex.WaitAny(Mutex[])”);

    Mutex[] gMs = new Mutex[2];

    gMs[0] = gM一;//创立贰个Mutex数组作为Mutex.WaitAny()方法的参数

    gMs[1] = gM2;

    Mutex.WaitAny(gMs);//等待数组中随心所欲多个Mutex对象被假释

    Console.WriteLine(“t3Start finished, Mutex.WaitAny(Mutex[])”);

    伊芙nt3.Set( );//线程截至,将伊芙nt三装置为有连续信号状态

  }

  public void t4Start( )

  {

    Console.WriteLine(“t4Start started, gM2.WaitOne( )”);

    gM2.WaitOne( );//等待gM2被释放

    Console.WriteLine(“t4Start finished, gM2.WaitOne( )”);

    伊芙nt4.Set( );//线程结束,将伊芙nt四设置为有实信号状态

  }

  }

  上面是该程序的举办结果:

  从履行结果能够很理解地看看,线程t二,t3的运营是以gM1的获释为准绳的,而t四在gM2释放后初叶推行,t一则在gM1和gM二都被放飞了以后才实施。Main()函数最终,使用WaitHandle等待全体的AutoReset伊夫nt对象的实信号,这一个指标的非确定性信号代表相应线程的收尾。

  六、小结

  二十四线程程序设计是多少个大幅的核心,而本文试图在.net
Framework环境下,使用最新的C#语言来描述二十多线程程序的概略。希望本文能带动大家掌握线程那种概念,精晓十二线程的用途,了解它的C#落到实处方式,掌握线程将为我们带来的便宜和麻烦。C#是一种新的言语,由此它的线程机制也有诸多破例的位置,希望我们能透过本文清楚地来看那几个,从而得以对线程举行更加深远的驾驭和商讨。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图