澳门至尊网站-首页

您的位置:澳门至尊网站 > 免费资源 > 线程基本概念,1创建线程

线程基本概念,1创建线程

2019-10-24 02:17

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
//创立线程
//两组范围为1-10的数字会随机交叉输出,表达PrintNumbers方法同期运营在主线程和另贰个线程中
namespace Recipe1
{
class Program
{
static void Main(string[] args)
{
Thread th = new Thread(PrintNumbers);
th.Start();
PrintNumbers();
Console.ReadKey();
}
static void PrintNumbers()
{
Console.WriteLine("Starting");
for (int i = 1; i < 10; i++)
{
Console.WriteLine(i);
}
}
}
}

原文:

 

引言

  随着双核、四核等多核管理器的扩充,多核管理器或超线程单核管理器的计算机已很遍布,基于多核管理的编制程序能力也初阶面对程序猿们分布关注。那当中八个主要的上边正是营造八线程应用程序(因为不应用八线程的话,开垦人士就不能够丰裕发挥多核管理器的强有力品质)。

  本文针对的是营造基于单核Computer的十二线程应用程序,意在介绍多线程相关的基本概念、内涵,以致怎样通过System.Threading命名空间的类、委托和BackgroundWorker组件等三种手腕营造四线程应用程序。

  本文假诺能为刚接触四线程的爱侣起到投砾引珠的意义也就高兴了。当然,本人才疏学浅,文中难免会有欠缺或不当的地点,恳请各位朋友多多指引。

  1.清楚三十二线程

  我们司空见惯通晓的应用程序就是一个*.exe文件,当运行*.exe应用程序将来,系统会在内部存款和储蓄器中为该程序分配一定的长空,同期加载一些该程序所需的财富。其实那就足以称为创造了三个历程,能够因而Windows职务管理器查看那个进程的连锁音信,如影像名称、顾客名、内部存款和储蓄器使用、PID(唯生龙活虎的进度标示)等,如图下所示。

    

  而线程则只是经过中的贰个基本进行单元。一个应用程序往往独有三个顺序入口,如:

  [STAThread]

  static void Main()   //应用程序主入口点

  {

  Application.EnableVisualStyles();

  Application.SetCompatibleTextRenderingDefault(false);

  Application.Run(new MainForm());

  }

  进度会含有一个跻身此入口的线程,大家称为主线程。在那之中,天性 [STAThread] 提示应用程序的暗中认可线程模型是单线程单元(相关新闻可参看http://msdn.microsoft.com/en-us/library/system.stathreadattribute(VS.71).aspx.aspx))。只含有一个主线程的进度是线程安全的,相当于程序独有一条专业线,唯有达成了前面包车型客车任务技术实行排在后边的职分。

  然当在程序管理贰个很耗费时间的职责,如输出叁个大的文件或远程访问数据库等,此时的窗体分界面程序对顾客来讲基本像是没反应同样,菜单、按键等都用持续。因为窗体上控件的响应事件也是亟需主线程来施行的,而主线程正忙着干任何的事,控件响应事件就只可以排队等着主线程忙完了再实施。

  为了征服单线程的这一个毛病,Win32 API能够让主线程再次创下造别的的次线程,但无论是主线程照旧次线程都以经过中独立的进行单元,能够而且访谈分享的数目,那样就有了四十多线程这几个概念。

  相信到那,应该对多线程有个比较感性的认知了。但小编在此要唤醒一下,基于单核Computer的多线程其实只是操作系统施展的三个障眼法而已(但那不会忧虑大家了解营造四线程应用程序的笔触),他并不能够减弱完毕具备任务的年华,不时反而还也许会因为运用过多的线程而减低品质、延长期。之所以这么,是因为对于单CPU来讲,在三个单位时间(也称时间片)内,只可以进行二个线程,即只好干意气风发件事。当三个线程的时间片用完时,系统会将该线程挂起,下一个时辰内再施行另八个线程,如此,CPU以时间片为间隔在多个线程之间轮换实施运算(其实这里还与各种线程的优先级有关,等第高的会事先管理)。由于交替时间间距相当的短,所以导致了逐条线程都在“同有的时候候”专门的学业的假象;而假诺线程数目过多,由于系统挂起线程时要记录线程当前的情形数据等,那样又势必会裁减程序的风姿罗曼蒂克体化质量。但对于那几个,多核管理器就能够从精气神上(真正的同一时间专业)进步程序的施行成效。

  2. 线程异步与线程同步

  从线程奉行任务的点子上能够分成线程同步和线程异步。而为了有助于理解,前边描述中用“同步线程”指代与线程同步相关的线程,同样,用“异步线程”表示与线程异步相关的线程。

  线程异步正是焚薮而田相通后面提到的施行耗时任务时分界面控件不可能运用的主题材料。如创造一个次线程去非常施行耗费时间的天职,而别的如界面控件响应那样的职分交给另三个线程施行(往往由主线程推行)。那样,七个线程之间通过线程调治器短期(时间片)内的切换,就模拟出五个任务“同期”被试行的成效。

  线程异步往往是经过创建多个线程实践四个职责,多少个工作线同偶然候开工,相像多辆在普及的公路上竞相的小车还要升高,互不烦懑(读者要驾驭,本质上并未有“同有的时候候”,仅仅是操作系统玩的二个障眼法。但那个障眼法却对增加大家的主次与客户之间的竞相、以致压实程序的友好性很有用,不是吧)。

  在介绍线程同步从前,先介绍一个与此紧凑有关的概念——并发问题。

  后边提到,线程都以独自的进行单元,可以访问分享的数目。也正是说,在多少个全数多少个次线程的次序中,每个线程都能够访谈同三个分享的数额。再稍加思考你会意识这样恐怕会出难点:由于线程调解器会随随意便的挂起某一个线程(后边介绍的线程间的切换),所以当线程a对分享数据D的访谈(改良、删除等操作)完毕从前被挂起,而此时线程b又恰好去探访数据D,那么线程b访谈的则是八个不安定的多少。那样就能够时有发生特别不便察觉bug,由于是轻松发生的,产生的结果是不足预测的,那样样的bug也都很难再次出现和调剂。那就是出新难题。

  为了解决多线程协作访谈三个分享能源(也称互斥访谈)时发出的面世难点,线程同步就现身了。线程同步的机理,简单来讲,便是防卫八个线程相同的时间做客有些分享的能源。做法超级粗略,标识访谈某分享财富的那部分代码,当程序运转到有号子的地点时,CL翼虎(具体是什么样能够先不管,只要了然它能说了算就行)对各线程实行调治:假设原来就有线程在做客一财富,CL奥迪Q5就能够将别的访谈这一能源的线程挂起,直到前一线程停止对该能源的拜见。那样就保障了同一时候唯有一个线程访谈该能源。打个借使,如同某财富位居独有生龙活虎独石桥相连的半壁江山上,即便要动用该能源,大家就得排队,二个三个来,前边的回到了,下叁个再去,前边的没回来,后边的就原地等待命令。

  这里只是把基本的定义及原理做了贰个简约的解说,不至于看前边的前后相继时糊里凌乱的。具体如何编写代码,上面包车型地铁段落将做详细介绍。

 

 

  3.创办多线程应用程序

  这里做一个简约的认证:上边首要透过介绍通过System.Threading命名空间的类、委托和BackgroundWorker组件两种区别的招数创设多线程应用程序,具体会从线程异步和线程同步八个方面来阐释。

  3.1透过System.Threading命名空间的类营造

  在.NET平台下,System.Threading命名空间提供了非常多类型来创设多线程应用程序,能够说是专为五十九线程服务的。由于本文仅是记忆到三个“引玉之砖”的意义,所以对于这一块不会追究过多、过深,重要选用System.Threading.Thread类。

  先从System.Threading.Thread类自己有关的贰个小例子说到,代码如下,解释见注释:

  using System;

  using System.Threading; //引进System.Threading命名空间

  namespace MultiThread

  {

  class Class

  {

  static void Main(string[] args)

  {

  Console.WriteLine("************** 呈现当前线程的连带消息 *************");

  //注明线程变量并赋值为近日线程

  Thread primaryThread = Thread.CurrentThread;

  //赋值线程的名号

  primaryThread.Name = "主线程";

  //展现线程的相关音信

  Console.WriteLine("线程的名字:{0}", primaryThread.Name);

  Console.WriteLine("线程是或不是运转? {0}", primaryThread.IsAlive);

  Console.WriteLine("线程的优先级: {0}", primaryThread.Priority);

  Console.WriteLine("线程的气象: {0}", primaryThread.ThreadState);

  Console.ReadLine();

  }

  }

  }

  输出结果如下:

  ************** 展现当前线程的有关新闻 *************

  线程的名字:主线程

  线程是不是运转? True

澳门至尊网站,  线程的事先级: Normal

  线程的动静: Running

  对于地点的代码不想做过多解释,只说一下Thread.CurrentThread获得的是推行当前代码的线程。

  3.1.1异步调用线程

  这里先说一下前台线程与后台线程。前台线程能挡住应用程序的甘休,既直到全部前台线程终止后才会透顶关闭应用程序。而对后台线程来说,当有着前台线程终止时,后台线程会被自动停止,无论后台线程是不是正在施行任务。默许景况下通过Thread.Start()方法创立的线程都活动为前台线程,把线程的习性IsBackground设为true时就将线程转为后台线程。

  下边先看两个事例,该例子创造一个次线程试行打字与印刷数字的天职,而主线程则干任何的事,两个同有的时候候举办,互不忧愁。

  using System;

  using System.Threading;

  using System.Windows.Forms;

  namespace MultiThread

  {

  class Class

  {

  static void Main(string[] args)

  {

  Console.WriteLine("************* 八个线程同一时候工作 *****************");

  //主线程,因为获得的是当下在试行Main()的线程

  Thread primaryThread = Thread.CurrentThread;

  primaryThread.Name = "主线程";

  Console.WriteLine("-> {0} 在施行主函数 Main()。", Thread.CurrentThread.Name);

  //次线程,该线程指向PrintNumbers()方法

  Thread SecondThread = new Thread(new ThreadStart(PrintNumbers));

  SecondThread.Name = "次线程";

  //次线程开端进行针对的法子

  SecondThread.Start();

  //同不平日候主线程在举办主函数中的别的任务

  MessageBox.Show("正在实行主函数中的职责。。。。", "主线程在职业...");

  Console.ReadLine();

  }

  //打字与印刷数字的方法

  static void PrintNumbers()

  {

  Console.WriteLine("-> {0} 在奉行打字与印刷数字函数 PrintNumber()", Thread.CurrentThread.Name);

  Console.WriteLine("打字与印刷数字: ");

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

  {

  Console.Write("{0}, ", i);

  //Sleep()方法使当前线程挂等待内定的时间长度在举办,这里首尽管模拟打字与印刷任务

  Thread.Sleep(2000);

  }

  Console.WriteLine();

  }

  }

  }

  程序运维后会见到贰个窗口弹出,如图所示,同一时候调节台窗口也在相连的来得数字。

    澳门至尊网站 1

  输出结果为:

  ************* 八个线程同期工作 *****************

  -> 主线程 在推行主函数 Main()。

  -> 次线程 在施行打字与印刷数字函数 PrintNumber()

  打字与印刷数字:

  0, 1, 2, 3, 4, 5, 6, 7, 8, 9,

 

上一页  [1] [2] [3] [4] [5] [6] 下一页

招待步入.NET社区论坛,与200万技艺人士互动交换>>步向

 

  这里稍稍对 Thread SecondThread = new Thread(new ThreadStart(PrintNumbers)); 这一句做个表明。其实 ThreadStart 是 System.Threading 命名空间下的一个信托,其宣称是 public delegate void ThreadStart(),指向不带参数、重临值为空的方式。所以当使用 ThreadStart 时,对应的线程就只好调用不带参数、再次来到值为空的法子。那非要指向含参数的办法吗?在System.Threading命名空间下还应该有一个ParameterizedThreadStart 委托,其宣称是 public delegate void ParameterizedThreadStart(object obj),能够针对含 object 类型参数的主意,这里并不是忘了 object 可是具备类其他父类哦,有了它就能够通过创办种种自定义类型,如协会、类等传递超多参数了,这里就不再例如表明了。

  3.1.2并发难题

  这里再通过几个事例让我们具体体会一下前方说起的产出难点,然后再介绍线程同步。

  using System;

  using System.Threading;

  namespace MultiThread1

  {

  class Class

  {

  static void Main(string[] args)

  {

  Console.WriteLine("********* 并发难点演示 ***************");

  //成立一个打字与印刷对象实例

  Printer printer = new Printer();

  //声雅培(Abbott)含5个线程对象的数组

  Thread[] threads = new Thread[10];

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

  {

  //将每贰个线程都针对printer的PrintNumbers()方法

  threads[i] = new Thread(new ThreadStart(printer.PrintNumbers));

  //给每二个线程编号

  threads[i].Name = i.ToString() +"号线程";

  }

  //开头施行全体线程

  foreach (Thread t in threads)

  t.Start();

  Console.ReadLine();

  }

  }

  //打印类

  public class Printer

  {

  //打字与印刷数字的点子

  public void PrintNumbers()

  {

  Console.WriteLine("-> {0} 正在执行打印任务,开首打字与印刷数字:", Thread.CurrentThread.Name);

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

  {

  Random r = new Random();

  //为了扩展冲突的可能率及,使各线程各自等待随机的时长

  Thread.Sleep(2000 * r.Next(5));

  //打字与印刷数字

  Console.Write("{0} ", i);

  }

  Console.WriteLine();

  }

  }

  }

  上边的例证中,主线程产生的13个线程同不经常候做客同贰个目的实例printer的形式PrintNumbers(),由于尚未锁定分享能源(注意,这里是指调整台),所以在PrintNumbers()输出到调整台以前,调用PrintNumbers()的线程很或然被挂起,但不知底什么样时候(或是还是不是有)挂起,导致得到不可预测的结果。如下是八个不等的结果(当然,读者的周转结果可能会是其余情况)。

    澳门至尊网站 2

  情形一

    澳门至尊网站 3

  情形二

 

 

 

  3.1.3线程同步

  线程同步的探访情势也称之为阻塞调用,即未有实行完任务不回来,线程被挂起。能够运用C#中的lock关键字,在那主要字范围类的代码都将是线程安全的。lock关键字需定义三个标记,线程走入锁定范围是必需取得这一个符号。当锁定的是八个实例级对象的个人方法时接受格局自身所在对象的援用就足以了,将上面例子中的打字与印刷类Printer稍做改革,增多lock关键字,代码如下:

  //打印类

  public class Printer

  {

  public void PrintNumbers()

  {

  //使用lock关键字,锁定d的代码是线程安全的

  lock (this)

  {

  Console.WriteLine("-> {0} 正在施行打字与印刷职责,开头打字与印刷数字:", Thread.CurrentThread.Name);

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

  {

  Random r = new Random();

  //为了扩展冲突的可能率及,使各线程各自等待随机的时长

  Thread.Sleep(2000 * r.Next(5));

  //打字与印刷数字

  Console.Write("{0} ", i);

  }

  Console.WriteLine();

  }

  }

  }

  }

  同步后进行结果如下:

    澳门至尊网站 4

  也得以利用System.Threading命名空间下的Monitor类实行联合,两个内涵是均等的,但Monitor类越来越灵敏,这里就不在做过多的斟酌,代码如下:

  //打印类

  public class Printer

  {

  public void PrintNumbers()

  {

  Monitor.Enter(this);

  try

  {

  Console.WriteLine("-> {0} 正在实施打印职务,在这里早先打字与印刷数字:", Thread.CurrentThread.Name);

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

  {

  Random r = new Random();

  //为了增加冲突的可能率及,使各线程各自等待随机的时间长度

  Thread.Sleep(2000 * r.Next(5));

  //打字与印刷数字

  Console.Write("{0} ", i);

  }

  Console.WriteLine();

  }

  finally

  {

  Monitor.Exit(this);

  }

  }

  }

  输出结果与地点的同样。

  3.2经过信托创设多线程应用程序

  在看上边包车型大巴内容时讲求对信托有自然的明白,倘若不清楚的话推荐参谋一下微博张子阳的《C# 中的委托和事件》,里面前碰着信托与事件张开按部就班的较系统的上书: http://www.cnblogs.com/JimmyZhang/archive/2007/09/23/903360.html。

  这里先举四个有关信托的简短例子,具体解释见注释:

  using System;

  namespace MultiThread

  {

  //定义三个指向性包涵七个int型参数、再次回到值为int型的函数的嘱托

  public delegate int AddOp(int x, int y);

  class Program

  {

  static void Main(string[] args)

  {

  //创制二个指向Add()方法的AddOp对象p

  AddOp pAddOp = new AddOp(Add);

  //使用委托直接调用方法Add()

  Console.WriteLine("10 + 25 = {0}", pAddOp(10, 5));

  Console.ReadLine();

  }

  //求和的函数

  static int Add(int x, int y)

  {

  int sum = x + y;

  return sum;

  }

  }

  }

  运维结果为:

  10 + 25 = 15

 

 

 

  3.2.1线程异步

  先说Bellamy下,这里不策动疏解委托线程异步或同台的参数字传送递、获取再次来到值等,只是做个普通的早先而已,要是前边临时光了再其它写后生可畏篇关于多线程中参数字传送递、获取重临值的随笔。

  注意观望地方的事例会发掘,间接行使委托实例 pAddOp(10, 5) 就调用了求和方式Add()。很刚毅,那些艺术是由主线程推行的。然则,委托项目中还会有此外三个章程——BeginInvoke()和EndInvoke(),上面通超过实际际的事例来证实,将上边包车型大巴例子做相符改造,如下:

  using System;

  using System.Threading;

  using System.Runtime.Remoting.Messaging;

  namespace MultiThread

  {

  //注脚指向含多个int型参数、再次回到值为int型的函数的寄托

  public delegate int AddOp(int x, int y);

  class Program

  {

  static void Main(string[] args)

  {

  Console.WriteLine("******* 委托异步线程 多少个线程“同期”职业 *********");

  //展现主线程的并世无两标示

  Console.WriteLine("调用Main()的主线程的线程ID是:{0}.", Thread.CurrentThread.ManagedThreadId);

  //将委托实例指向Add()方法

  AddOp pAddOp = new AddOp(Add);

  //起始委托次线程调用。委托BeginInvoke()方法再次回到的品类是IAsyncResult,

  //富含那委托指向方法甘休重临的值,同期也是EndInvoke()方法参数

  IAsyncResult iftAR = pAddOp.BeginInvoke(10, 10, null, null);

  Console.WriteLine(""nMain()方法中实行其它职务........"n");

  int sum = pAddOp.EndInvoke(iftAR);

  Console.WriteLine("10 + 10 = {0}.", sum);

  Console.ReadLine();

  }

  //求和办法

  static int Add(int x, int y)

  {

  //提示调用该办法的线程ID,ManagedThreadId是线程的有一无二标示

  Console.WriteLine("调用求和办法 Add()的线程ID是: {0}.", Thread.CurrentThread.ManagedThreadId);

  //模拟贰个过程,停留5秒

  Thread.Sleep(5000);

  int sum = x + y;

  return sum;

  }

  }

  }

  运转结果如下:

  ******* 委托异步线程 七个线程“同一时候”职业 *********

  调用Main()的主线程的线程ID是:10.

  Main()方法中举办其它任务........

  调用求和方法 Add()的线程ID是: 7.

  10 + 10 = 20.

  3.2.2线程同步

  委托中的线程同步首要涉及到地方运用的pAddOp.BeginInvoke(10, 10, null, null)方法中后边三个为null的参数,具体的能够参见相关质地。这里代码如下,解释见代码注释:

  using System;

  using System.Threading;

  using System.Runtime.Remoting.Messaging;

  namespace MultiThread

  {

  //证明指向含四个int型参数、再次来到值为int型的函数的嘱托

  public delegate int AddOp(int x, int y);

  class Program

  {

  static void Main(string[] args)

  {

  Console.WriteLine("******* 线程同步,“阻塞”调用,四个线程专门的学业 *********");

  Console.WriteLine("Main() invokee on thread {0}.", Thread.CurrentThread.ManagedThreadId);

  //将委托实例指向Add()方法

  AddOp pAddOp = new AddOp(Add);

  IAsyncResult iftAR = pAddOp.BeginInvoke(10, 10, null, null);

  //判别委托线程是还是不是奉行完职责,

  //未有完毕的话,主线程就做任何的事

  while (!iftAR.IsCompleted)

  {

  Console.WriteLine("Main()方法职业中.......");

  Thread.Sleep(1000);

  }

  //获得重临值

  int answer = pAddOp.EndInvoke(iftAR);

  Console.WriteLine("10 + 10 = {0}.", answer);

  Console.ReadLine();

  }

  //求和措施

  static int Add(int x, int y)

  {

  //提示调用该办法的线程ID,ManagedThreadId是线程的唯大器晚成标示

  Console.WriteLine("调用求和措施 Add()的线程ID是: {0}.", Thread.CurrentThread.ManagedThreadId);

  //模拟一个进度,停留5秒

  Thread.Sleep(5000);

  int sum = x + y;

  return sum;

  }

  }

  }

 

 

  运营结果如下:

  ******* 线程同步,“阻塞”调用,三个线程专门的学业 *********

  Main() invokee on thread 10.

  Main()方法职业中.......

  调用求和方式 Add()的线程ID是: 7.

  Main()方法职业中.......

  Main()方法职业中.......

  Main()方法工作中.......

  Main()方法工作中.......

  10 + 10 = 20.

  3.3BackgroundWorker组件

  BackgroundWorker组件位于工具箱中,用于方便的创设线程异步的次第。新建一个WindowsForms应用程序,分界面如下:

    澳门至尊网站 5

  代码如下,解释参见注释:

  private void button1_Click(object sender, EventArgs e)

  {

  try

  {

  //得到输入的数字

  int numOne = int.Parse(this.textBox1.Text);

  int numTwo = int.Parse(this.textBox2.Text);

  //实例化参数类

  AddParams args = new AddParams(numOne, numTwo);

  //调用RunWorkerAsync()生成后台线程,同期传入参数

  this.backgroundWorker1.RunWorkerAsync(args);

  }

  catch (Exception ex)

  {

  MessageBox.Show(ex.Message);

  }

  }

  //backgroundWorker新生成的线程最早工作

  private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)

  {

  //获取传入的AddParams对象

  AddParams args = (AddParams)e.Argument;

  //停留5秒,模拟耗费时间任务

  Thread.Sleep(5000);

  //返回值

  e.Result = args.a + args.b;

  }

  //当backgroundWorker1的DoWork中的代码奉行完后会触发该事件

  //同一时间,其实行的结果会含有在RunWorkerCompleted伊夫ntArgs参数中

  private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)

  {

  //展现运算结果

  MessageBox.Show("运营结果为:" + e.Result.ToString(), "结果");

  }

  }

  //参数类,那个类仅仅起到叁个记下并传递参数的效能

  class AddParams

  {

  public int a, b;

  public AddParams(int numb1, int numb2)

  {

  a = numb1;

  b = numb2;

  }

  }

  注意,在总结结果的同一时间,窗体能够随性所欲活动,也得以重新在文本框中输入音信,那就认证主线程与backgroundWorker组件生成的线程是异步的。

  4.总结

  本文从线程、进度、应用程序的涉及开首,介绍了部分有关四线程的基本概念,同不经常候演说了线程异步、线程同步及现身难点等。最终从使用角度出发,介绍了怎么通过System.Threading命名空间的类、委托和BackgroundWorker组件等二种花招营造多线程应用程序。

本文由澳门至尊网站发布于免费资源,转载请注明出处:线程基本概念,1创建线程

关键词:

  • 上一篇:没有了
  • 下一篇:没有了