All articles| All Pictures| All Softwares| All Video| Go home page| Write articles| Upload pictures

Reading number is top 10 articles
当ASP.NET撞上JSF之应用程序安全性_[Asp.Net教程]
C#教程:WebClient类使用实例
MFC应用程序的开发流程
跟我学SQL:(七)从子表里删除数据_mssql学习_编程技术
使用MD5变换算法来防止穷举破译密码_[Asp.Net教程]
ASP.NET,MVC,Framework体验(3):表单提交_[Asp.Net教程]
XMLHTTP对象应用开发的初体验_[XML教程]
aspx页面中Request读取字符成乱码或不准_[Asp.Net教程]
动态网页技巧:PHP,MySQL,integration_php资料_编程技术
常规数据库维护涉及的5项工作_[SQL Server教程]
Reading number is top 10 pictures
沙漠里的美女
The money of more than 100 countries and regions14
你是左脑型还是右脑型
这是男生笨么?
Catch prostitution woman in China
On the verge of extinction of the beach1
如果我是导演...
The terra-cotta warriors2
运动的范冰冰3
The money of more than 100 countries and regions20
Download software ranking
尖东毒玫瑰B
C#与.NET技术平台实战演练
SQL2000 For 4IN1
Eclipse-CALMSANNY (second edition)
塘西风月痕
功夫熊猫2(上集)
Tram sex maniac 2 (H) rar bag2
Popkart Cracked versions Mobile phone games
Tram sex maniac 2 (H) rar bag12
linux初级教程
aaa published in(发表于) 2013/12/18 8:12:53 Edit(编辑)
微软.Net开发中的多线程编程总结_.net资料_编程技术

微软.Net开发中的多线程编程总结_.net资料_编程技术

微软.Net开发中的多线程编程总结_.net资料_编程技术-你的首页-uuhomepage.com

  本文将对微软.Net开发中的多线程编程进行一个简单的总结,相信对大家会有所帮助的。下面就具体来看看:


  不需要传递参数,也不需要返回参数


  我们知道启动一个线程最直观的办法是使用Thread类,具体步骤如下:


ThreadStart threadStart=new ThreadStart(Calculate);
Thread thread=new Thread(threadStart);
  thread.Start();
  public void Calculate(){
  double Diameter=0.5;
  Console.Write("The Area Of Circle with a Diameter of {0} is {1}"Diameter,Diameter*Math.PI);
  }


  上面我们用定义了一个ThreadStart类型的委托,这个委托制定了线程需要执行的方法:Calculate,在这个方法里计算了一个直径为0.5的圆的面积,并输出.这就构成了最简单的多线程的例子,在很多情况下这就够用了,然后ThreadStart这个委托定义为void ThreadStart(),也就是说,所执行的方法不能有参数,这显然是个很大的不足,为了弥补这个缺陷,聪明的程序员想出了许多好的方法,我们将在需要传递多个参数一节中进行介绍,这里我们先介绍.Net为了解决这个问题而设定的另外一个委托:就是ParameterizedThreadStart ,我会在下面详细讲述。


  需要传递单个参数


ParameterThreadStart的定义为void ParameterizedThreadStart(object state)??使用这个这个委托定义的线程的启动函数可以接受一个输入参数,具体例子如下
  ParameterizedThreadStart threadStart=new ParameterizedThreadStart(Calculate)
  Thread thread=new Thread()
  thread.Start(0.9);
  public void Calculate(object arg){
  double Diameter=double(arg);
  Console.Write("The Area Of Circle with a Diameter of {0} is {1}"Diameter,Diameter*Math.PI);
  }


  例2


  Calculate方法有一个为object类型的参数,虽然只有一个参数,而且还是object类型的,使用的时候尚需要类型转换,但是好在可以有参数了,并且通过把多个参数组合到一个类中,然后把这个类的实例作为参数传递,就可以实现多个参数传递


  需要传递多个参数


  虽然通过把需要的参数包装到一个类中,委托ParameterizedThreadStart就可以传递多个参数,但是由于这个委托的传入参数是object,所以不可避免的需要进行参数转换,下面还有几个常用的参数传递方法,让我们来一一看来


  使用专门的线程类


  这是许多程序员爱使用的经典模式,简单来说,就是把需要另起线程执行的方法,和他需要的参数放到一个类中,参数作为了类的属性,调用时声明此类的实例,然后初始化属性,方法执行时直接使用类里初始化好的属性来执行,这样方法本身就可以不需要参数,而又起到了多参数传递的效果,于是使用本文最开始提到的不带参数的ThreadStart委托就可以了,并且由于需要执行的方法和参数都放在一个类中,充分体现了面向对象的特点.具体方法如下


  还是计算面积的方法的例子,我们把这个方法用一个类包装起来,输入参数Diameter(直径)是这个类的一个字段


public class MyThread
  {
  public double Diameter=10;
  public double Result=0;
  public MyThread(int Diameter)
  {
  this.Diameter = Diameter;
  }
  public void Calculate()
  {
  Console.WriteLine( "Calculate Start");
  Thread.Sleep(2000);
  Result = Diameter*Math.PI;;
  Console.WriteLine("Calculate End, Diameter is {0},Result is {1}" ,this.Diameter, Result);
  }
  }
  MyThread t=new MyThread(5.0);
  ThreadStart threadStart=new ThreadStart(t.Calculate)
  Thread thread=new Thread(threadStart);
  thread.Start();


  例3


  这种方法把参数传递变成了属性共享,想传递多少个变量都可以,从封装上讲,把逻辑和逻辑涉及的数据封装在一起,也很不错,这个方法还有一个聪明的变体,利用了匿名方法,这种变体连独立的类都省掉了,我现在给出这个方法


double Diameter = 6;
  double Result=0;
  Thread ta = new Thread(new ThreadStart(delegate()
  {
  Thread.Sleep(2000);
  Result=Diameter * Math.PI;
  Console.WriteLine("匿名 Calculate End, Diameter is {0},Result is {1}", Diameter, Result); ;
  }));
  ta.Start();


  例4


  这个方法和上例道理相同,都是把参数传递变成了对变量的调用,从而取消了参数传递,但是,后者充分利用了匿名方法的一个性质,就是可以直接使用当前上下文的局部变量,比如委托中的Diameter,和Result.当然,这样做的缺点是如果匿名方法太长,程序的可读性会降低,所以一般很少有人这样做,这里给出这个方法供大家参考。


  聪明的读者肯定想,既然可以用字段来传入变量,当然也可以用字段传出变量,比如在上面两个例子里我们看到计算结果都写进了一个叫Result(加亮的地方)的变量里,我们直接访问这个变量不就可以得到计算结果了吗?


  这样做有一个致命的问题:既然是异步执行,主线程怎么知道分线程什么时候完成了计算呢?比如上两个例子中,我们的线程都睡眠了2000毫秒,然后才进行计算,那么如果主线程在没有完成计算前访问Result,只能得到一个0值.于是我们就有了下面的一系列解决方法.


  需要传递参数且需要返回参数


  刚才说到主线程需要知道子线程什么时候执行完成,可以使用Thread.ThreadState枚举来判断。


  当线程的ThreadState==ThreadState.Stop时,一般就说明线程完成了工作,这时结果就可用了,如果不是这个状态,就继续执行别的工作,或者等待一会,然后再尝试.倘若需要等有多个子线程需的返回,并且需要用他们的结果来进行进异步计算,那就叫做线程同步了,下面我们介绍另外一种我比较推荐的方法,能够自定义参数个数,并且返回数据,而且使用起来也相对方便


  使用委托的异步调用方法和回调


  首先我们要把需要异步调用的方法定义为一个委托,然后利用BeginInvoke来异步调用,BeginInvoke的第一个参数就是直径,第二个是当线程执行完毕后的调用的方法。


delegate double CalculateMethod(double Diameter);
  static CalculateMethod calcMethod;
  double result = 0;
  static void Main(string[] args)
  {
  calcMethod = new CalculateMethod(Calculate);
  calcMethod.BeginInvoke(5, new AsyncCallback(TaskFinished), null);
  }
  ///
  ///线程调用的函数
  ///
  public static double Calculate(double Diameter)
  {
  return Diameter * Math.PI;
  }
  ///
  ///线程完成之后回调的函数
  ///
  public static void TaskFinished(IAsyncResult result)
  {
  result=calcMethod.EndInvoke(result);
  }


  例5


  注意,再线程执行完毕后执行的方法TaskFinished中,我们使用了EndInvoke来取得这个函数的返回值。


  线程池


  线程虽然是个好东西,但是也是个资源消耗大户,许多时候,我们需要用多线程,但是又不希望线程的数量过多,这就是线程池的作用,.Net为我们提供了现成的线程池ThreadPool,他的使用如下:


WaitCallback w = new WaitCallback(Calculate);
  ThreadPool.QueueUserWorkItem(w, 1.0);
  ThreadPool.QueueUserWorkItem(w, 2.0);
  ThreadPool.QueueUserWorkItem(w, 3.0);
  ThreadPool.QueueUserWorkItem(w, 4.0);
  public static void Calculate(double Diameter)
  {
  return Diameter * Math.PI;
  }


  例6


  首先定义一个WaitCallback委托,WaitCallback的格式是void WaitCallback(object state),也就是说你的方法必须符合这个格式,接着调用QueueUserWorkItem,将这个任务加入线程池,当县城池有空闲线时,将会调度并运行你的代码。


  每一个进程都有一个线程池,线程池的默认大小是25,我们可以通过SetMaxThreads方法来设置其最大值.


  [注]由于每个进程只有一个线程池,所以如果是在iis进程,或者sqlserver的进程中使用线程池,并且需要设置线程池的最大容量的话,会影响到iis进程或sql进程,所以这两种情况下要特别小心。


  控制权


  在和大家交谈的时候我发现凡是习惯了面向对象思维的同事,总是对多线程情况下的执行上下文很困扰,比如例5中,主程序启动了子线程执行Calculate方法,执行完毕后回调TaskFinished,假如主线程id是1,子线程id是2,那么Calculate肯定是在id=2的线程中执行,那么他的回调函数TaskFinished呢? 同样也是在id=2的线程上下文中执行,不信你输出线程id试试,这通常不是什么问题,但是当我们需要在Winform编程中使用子线程时,就有可能会引起问题了,我们将在下面讲这个问题。


  窗体程序多线程编程的特殊性


  当我们把例5的回调代码稍加修改,搬到winform里面,就可以看到问题所在了


public static void TaskFinished(IAsyncResult result)
  {
  result=calcMethod.EndInvoke(result);
  this.TextBox1.Text=result;
  }


  程序的原意是在线程执行完毕后讲结果写入一个TextBox,然而当程序执行到this.TextBox1.Text=result这里的时候就抱错了.原来WinForm对线程有很严格的要求,除了创建这些控件的线程,其他线程想跨线程访问WinForm上的控件的属性和方法是不允许(除了几个特殊属性),在有的版本系统上,比如XP,对这个问题进行了处理,跨线程控件访问可以被执行,但是大多数windows系统都是不可以的,那么如果我们确实需要跨线程修改控件属性,或者调用控件的方法,就必须用到控件的一个方法Invoke,这个方法可以把执行上下文切换回创建这些控件的线程,具体操作如下:


delegate void changeText(string result);
  public static void TaskFinished(IAsyncResult result)
  {
  result=calcMethod.EndInvoke(result);
  this.BeginInvoke(new changeText(this.textBox1.AppendText),t.Result.ToString())
  }


  由于委托中必须使用方法,所以我用AppendText方法,而不是直接设置Text属性,你如果想设置text属性,就必须自己包装一个方法,然后连接到委托了。





添加到del.icio.us 添加到新浪ViVi 添加到百度搜藏 添加到POCO网摘 添加到天天网摘365Key 添加到和讯网摘 添加到天极网摘 添加到黑米书签 添加到QQ书签 添加到雅虎收藏 添加到奇客发现 diigo it 添加到饭否 添加到飞豆订阅 添加到抓虾收藏 添加到鲜果订阅 digg it 貼到funP 添加到有道阅读 Live Favorites 添加到Newsvine 打印本页 用Email发送本页 在Facebook上分享


Disclaimer Privacy Policy About us Site Map

If you have any requirements, please contact webmaster。(如果有什么要求,请联系站长)
Copyright ©2011-
uuhomepage.com, Inc. All rights reserved.