我已看了一些多线程的基础,能够写一些简单的例子,
但是想要前一步提高,请各位大侠帮帮小弟。
谢谢啦~~因为这边不能上外网,有什么好文/url贴出来。

解决方案 »

  1.   

    http://blog.csdn.net/knight94/archive/2006/05/27/757351.aspx
      

  2.   

    多线程是许多操作系统所具有的特性,它能大大提高程序的运行效率,所以多线程编程技术为编程者广泛关注。目前微软的.Net战略正进一步推进,各种相关的技术正为广大编程者所接受,同样在.Net中多线程编程技术具有相当重要的地位。本文我就向大家介绍在.Net下进行多线程编程的基本方法和步骤。   
      开始新线程 
      在.Net下创建一个新线程是非常容易的,你可以通过以下的语句来开始一个新的线程:  
      Thread thread = new Thread (new ThreadStart (ThreadFunc)); 
      thread.Start (); 
     
      第一条语句创建一个新的Thread对象,并指明了一个该线程的方法。当新的线程开始时,该方法也就被调用执行了。该线程对象通过一个System..Threading.ThreadStart类的一个实例以类型安全的方法来调用它要调用的线程方法。 
     
      第二条语句正式开始该新线程,一旦方法Start()被调用,该线程就保持在一个"alive"的状态下了,你可以通过读取它的IsAlive属性来判断它是否处于"alive"状态。下面的语句显示了如果一个线程处于"alive"状态下就将该线程挂起的方法: 
      if (thread.IsAlive) {   
      thread.Suspend ();  
      }    
      不过请注意,线程对象的Start()方法只是启动了该线程,而并不保证其线程方法ThreadFunc()能立即得到执行。它只是保证该线程对象能被分配到CPU时间,而实际的执行还要由操作系统根据处理器时间来决定。   
      一个线程的方法不包含任何参数,同时也不返回任何值。它的命名规则和一般函数的命名规则相同。它既可以是静态的(static)也可以是非静态的(nonstatic)。当它执行完毕后,相应的线程也就结束了,其线程对象的IsAlive属性也就被置为false了。下面是一个线程方法的实例: 
      public static void ThreadFunc()   
      {  
      for (int i = 0; i <10; i++) {   
      Console.WriteLine("ThreadFunc {0}", i);  
      }  
      }  
      前台线程和后台线程  
      .Net的公用语言运行时(Common Language Runtime,CLR)能区分两种不同类型的线程:前台线程和后台线程。这两者的区别就是:应用程序必须运行完所有的前台线程才可以退出;而对于后台线程,应用程序则可以不考虑其是否已经运行完毕而直接退出,所有的后台线程在应用程序退出时都会自动结束。  
      一个线程是前台线程还是后台线程可由它的IsBackground属性来决定。这个属性是可读又可写的。它的默认值为false,即意味着一个线程默认为前台线程。我们可以将它的IsBackground属性设置为true,从而使之成为一个后台线程。   
      下面的例子是一个控制台程序,程序一开始便启动了10个线程,每个线程运行5秒钟时间。由于线程的IsBackground属性默认为false,即它们都是前台线程,所以尽管程序的主线程很快就运行结束了,但程序要到所有已启动的线程都运行完毕才会结束。示例代码如下:  
      using System; 
      using System.Threading;   
      class MyApp  
      {   
      public static void Main ()  
      {   
      for (int i=0; i<10; i++) {   
      Thread thread = new Thread (new ThreadStart (ThreadFunc));   
      thread.Start ();   
      }  
      }  
      private static void ThreadFunc ()  
      {  
      DateTime start = DateTime.Now;   
      while ((DateTime.Now - start).Seconds <5)  
      ;    
      }    
      } 
         
      接下来我们对上面的代码进行略微修改,将每个线程的IsBackground属性都设置为true,则每个线程都是后台线程了。那么只要程序的主线程结束了,整个程序也就结束了。示例代码如下: 
       
      using System;  
      using System.Threading;  
      class MyApp  
      {   
      public static void Main ()  
      {   
      for (int i=0; i<10; i++) { 
      Thread thread = new Thread (new ThreadStart (ThreadFunc));    
      thread.IsBackground = true;    
      thread.Start ();   
      }   
      }   
      private static void ThreadFunc ()   
      {  
      DateTime start = DateTime.Now;   
      while ((DateTime.Now - start).Seconds <5) ;   
      }   
      }   
      既然前台线程和后台线程有这种差别,那么我们怎么知道该如何设置一个线程的IsBackground属性呢?下面是一些基本的原则:对于一些在后台运行的线程,当程序结束时这些线程没有必要继续运行了,那么这些线程就应该设置为后台线程。比如一个程序启动了一个进行大量运算的线程,可是只要程序一旦结束,那个线程就失去了继续存在的意义,那么那个线程就该是作为后台线程的。而对于一些服务于用户界面的线程往往是要设置为前台线程的,因为即使程序的主线程结束了,其他的用户界面的线程很可能要继续存在来显示相关的信息,所以不能立即终止它们。这里我只是给出了一些原则,具体到实际的运用往往需要编程者的进一步仔细斟酌。   
      线程优先级   
      一旦一个线程开始运行,线程调度程序就可以控制其所获得的CPU时间。如果一个托管的应用程序运行在Windows机器上,则线程调度程序是由Windows所提供的。在其他的平台上,线程调度程序可能是操作系统的一部分,也自然可能是.Net框架的一部分。不过我们这里不必考虑线程的调度程序是如何产生的,我们只要知道通过设置线程的优先级我们就可以使该线程获得不同的CPU时间。 
        
      线程的优先级是由Thread.Priority属性控制的,其值包含:ThreadPriority.Highest、ThreadPriority.AboveNormal、ThreadPriority.Normal、ThreadPriority.BelowNormal和ThreadPriority.Lowest。从它们的名称上我们自然可以知道它们的优先程度,所以这里就不多作介绍了。 
      线程的默认优先级为ThreadPriority.Normal。理论上,具有相同优先级的线程会获得相同的CPU时间,不过在实际执行时,消息队列中的线程阻塞或是操作系统的优先级的提高等原因会导致具有相同优先级的线程会获得不同的CPU时间。不过从总体上来考虑仍可以忽略这种差异。你可以通过以下的方法来改变一个线程的优先级。 
      thread.Priority = ThreadPriority.AboveNormal; 
      或是:   thread.Priority = ThreadPriority.BelowNormal; 
      通过上面的第一句语句你可以提高一个线程的优先级,那么该线程就会相应的获得更多的CPU时间;通过第二句语句你便降低了那个线程的优先级,于是它就会被分配到比原来少的CPU时间了。你可以在一个线程开始运行前或是在它的运行过程中的任何时候改变它的优先级。理论上你还可以任意的设置每个线程的优先级,不过一个优先级过高的线程往往会影响到其他线程的运行,甚至影响到其他程序的运行,所以最好不要随意的设置线程的优先级。 
      挂起线程和重新开始线程  
      Thread类分别提供了两个方法来挂起线程和重新开始线程,也就是Thread.Suspend能暂停一个正在运行的线程,而Thread.Resume又能让那个线程继续运行。不像Windows内核,.Net框架是不记录线程的挂起次数的,所以不管你挂起线程过几次,只要一次调用Thread.Resume就可以让挂起的线程重新开始运行。  
      Thread类还提供了一个静态的Thread.Sleep方法,它能使一个线程自动的挂起一定的时间,然后自动的重新开始。一个线程能在它自身内部调用Thread.Sleep方法,也能在自身内部调用Thread.Suspend方法,可是一定要别的线程来调用它的Thread.Resume方法才可以重新开始。这一点是不是很容易想通的啊?下面的例子显示了如何运用Thread.Sleep方法: 
      while (ContinueDrawing) {  
      DrawNextSlide ();  
      Thread.Sleep (5000);  
      } 
     
      

  3.   

    终止线程  
      在托管的代码中,你可以通过以下的语句在一个线程中将另一个线程终止掉:  
      thread.Abort (); 
      下面我们来解释一下Abort()方法是如何工作的。因为公用语言运行时管理了所有的托管的线程,同样它能在每个线程内抛出异常。Abort()方法能在目标线程中抛出一个ThreadAbortException异常从而导致目标线程的终止。不过Abort()方法被调用后,目标线程可能并不是马上就终止了。因为只要目标线程正在调用非托管的代码而且还没有返回的话,该线程就不会立即终止。而如果目标线程在调用非托管的代码而且陷入了一个死循环的话,该目标线程就根本不会终止。不过这种情况只是一些特例,更多的情况是目标线程在调用托管的代码,一旦Abort()被调用那么该线程就立即终止了。  
      在实际应用中,一个线程终止了另一个线程,不过往往要等那个线程完全终止了它才可以继续运行,这样的话我们就应该用到它的Join()方法。示例代码如下: 
      thread.Abort (); // 要求终止另一个线程 
      thread.Join (); // 只到另一个线程完全终止了,它才继续运行 
      但是如果另一个线程一直不能终止的话(原因如前所述),我们就需要给Join()方法设置一个时间限制,方法如下: 
      thread.Join (5000); // 暂停5秒  
      这样,在5秒后,不管那个线程有没有完全终止,本线程就强行运行了。该方法还返回一个布尔型的值,如果是true则表明那个线程已经完全终止了,而如果是false的话,则表明已经超过了时间限制了。 
      时钟线程 
      .Net框架中的Timer类可以让你使用时钟线程,它是包含在System.Threading名字空间中的,它的作用就是在一定的时间间隔后调用一个线程的方法。下面我给大家展示一个具体的实例,该实例以1秒为时间间隔,在控制台中输出不同的字符串,代码如下: 
      using System;    
      using System.Threading;   
      class MyApp  
      {  
      private static bool TickNext = true; 
      public static void Main ()   
      {  
      Console.WriteLine ("Press Enter to terminate...");   
      TimerCallback callback = new TimerCallback (TickTock);   
      Timer timer = new Timer (callback, null, 1000, 1000);  
      Console.ReadLine ();   
      }   
      private static void TickTock (object state)   
      {   
      Console.WriteLine (TickNext ? "Tick" : "Tock");   
      TickNext = ! TickNext;   
      }   
      }    
      从上面的代码中,我们知道第一个函数回调是在1000毫秒后才发生的,以后的函数回调也是在每隔1000毫秒之后发生的,这是由Timer对象的构造函数中的第三个参数所决定的。程序会在1000毫秒的时间间隔后不断的产生新线程,只到用户输入回车才结束运行。不过值得注意的是,虽然我们设置了时间间隔为1000毫秒,但是实际运行的时候往往并不能非常精确。因为Windows操作系统并不是一个实时系统,而公用语言运行时也不是实时的,所以由于线程调度的千变万化,实际的运行效果往往是不能精确到毫秒级的,但是对于一般的应用来说那已经是足够的了,所以你也不必十分苛求。   
      小结    
      本文介绍了在.Net下进行多线程编程所需要掌握的一些基本知识。从文章中我们可以知道在.Net下进行多线程编程相对以前是有了大大的简化,但是其功能并没有被削弱。使用以上的一些基本知识,读者就可以试着编写.Net下的多线程程序了。不过要编写出功能更加强大而且Bug少的多线程应用程序,读者需要掌握诸如线程同步、线程池等高级的多线程编程技术。读者不妨参考一些操作系统方面或是多线程编程方面的技术丛书。
      

  4.   

    转一个面这个例子使用了Mutex对象来同步四个线程,主线程等待四个线程的结束,而这四个线程的运行又是与两个Mutex对象相关联的。其中还用到AutoResetEvent类的对象,如同上面提到的ManualResetEvent对象一样,大家可以把它简单地理解为一个信号灯,使用AutoResetEvent.Set()方法可以设置它为有信号状态,而使用AutoResetEvent.Reset()方法把它设置为无信号状态。这里用它的有信号状态来表示一个线程的结束。&#58853;&#58853;// Mutex.cs
    &#58853;&#58853;using System;
    &#58853;&#58853;using System.Threading;&#58853;&#58853;public class MutexSample
    &#58853;&#58853;{
      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)
      {
      &#58853;&#58853;Console.WriteLine("Mutex Sample ...");
      &#58853;&#58853;//创建一个Mutex对象,并且命名为MyMutex
      &#58853;&#58853;gM1 = new Mutex(true,"MyMutex");
      &#58853;&#58853;//创建一个未命名的Mutex 对象.
      &#58853;&#58853;gM2 = new Mutex(true);
      &#58853;&#58853;Console.WriteLine(" - Main Owns gM1 and gM2");  &#58853;&#58853;AutoResetEvent[] evs = new AutoResetEvent[4];
      &#58853;&#58853;evs[0] = Event1; file://为后面的线程t1,t2,t3,t4定义AutoResetEvent对象
      &#58853;&#58853;evs[1] = Event2; 
      &#58853;&#58853;evs[2] = Event3; 
      &#58853;&#58853;evs[3] = Event4;   &#58853;&#58853;MutexSample tm = new MutexSample( );
      &#58853;&#58853;Thread t1 = new Thread(new ThreadStart(tm.t1Start));
      &#58853;&#58853;Thread t2 = new Thread(new ThreadStart(tm.t2Start));
      &#58853;&#58853;Thread t3 = new Thread(new ThreadStart(tm.t3Start));
      &#58853;&#58853;Thread t4 = new Thread(new ThreadStart(tm.t4Start));
      &#58853;&#58853;t1.Start( );// 使用Mutex.WaitAll()方法等待一个Mutex数组中的对象全部被释放
      &#58853;&#58853;t2.Start( );// 使用Mutex.WaitOne()方法等待gM1的释放
      &#58853;&#58853;t3.Start( );// 使用Mutex.WaitAny()方法等待一个Mutex数组中任意一个对象被释放
      &#58853;&#58853;t4.Start( );// 使用Mutex.WaitOne()方法等待gM2的释放
      &#58853;&#58853;Thread.Sleep(2000);
      &#58853;&#58853;Console.WriteLine(" - Main releases gM1");
      &#58853;&#58853;gM1.ReleaseMutex( ); file://线程t2,t3结束条件满足  &#58853;&#58853;Thread.Sleep(1000);
      &#58853;&#58853;Console.WriteLine(" - Main releases gM2");
      &#58853;&#58853;gM2.ReleaseMutex( ); file://线程t1,t4结束条件满足  &#58853;&#58853;//等待所有四个线程结束
      &#58853;&#58853;WaitHandle.WaitAll(evs); 
      &#58853;&#58853;Console.WriteLine("... Mutex Sample");
      &#58853;&#58853;Console.ReadLine();
      }  public void t1Start( )
      {
      &#58853;&#58853;Console.WriteLine("t1Start started, Mutex.WaitAll(Mutex[])");
      &#58853;&#58853;Mutex[] gMs = new Mutex[2];
      &#58853;&#58853;gMs[0] = gM1;//创建一个Mutex数组作为Mutex.WaitAll()方法的参数
      &#58853;&#58853;gMs[1] = gM2;
      &#58853;&#58853;Mutex.WaitAll(gMs);//等待gM1和gM2都被释放
      &#58853;&#58853;Thread.Sleep(2000);
      &#58853;&#58853;Console.WriteLine("t1Start finished, Mutex.WaitAll(Mutex[]) satisfied");
      &#58853;&#58853;Event1.Set( ); file://线程结束,将Event1设置为有信号状态
      }  public void t2Start( )
      {
      &#58853;&#58853;Console.WriteLine("t2Start started, gM1.WaitOne( )");
      &#58853;&#58853;gM1.WaitOne( );//等待gM1的释放
      &#58853;&#58853;Console.WriteLine("t2Start finished, gM1.WaitOne( ) satisfied");
      &#58853;&#58853;Event2.Set( );//线程结束,将Event2设置为有信号状态
      }  public void t3Start( )
      {
      &#58853;&#58853;Console.WriteLine("t3Start started, Mutex.WaitAny(Mutex[])");
      &#58853;&#58853;Mutex[] gMs = new Mutex[2];
      &#58853;&#58853;gMs[0] = gM1;//创建一个Mutex数组作为Mutex.WaitAny()方法的参数
      &#58853;&#58853;gMs[1] = gM2;
      &#58853;&#58853;Mutex.WaitAny(gMs);//等待数组中任意一个Mutex对象被释放
      &#58853;&#58853;Console.WriteLine("t3Start finished, Mutex.WaitAny(Mutex[])");
      &#58853;&#58853;Event3.Set( );//线程结束,将Event3设置为有信号状态
      }  public void t4Start( )
      {
      &#58853;&#58853;Console.WriteLine("t4Start started, gM2.WaitOne( )");
      &#58853;&#58853;gM2.WaitOne( );//等待gM2被释放
      &#58853;&#58853;Console.WriteLine("t4Start finished, gM2.WaitOne( )");
      &#58853;&#58853;Event4.Set( );//线程结束,将Event4设置为有信号状态
      }
    &#58853;&#58853;} 
      

  5.   

    真正的多线程并不好用,特别是在NET这种应用级的开发中。资源的锁定与释放是开发中的恶梦一般多线程用在算法中,或系统,服务的底层开发。当然NET中也可用,如例子中的后台打印什么的。在NET的应用级开发中,要提高性能,务实一些,可以使用多进程的方式来替代多线程
    比如
    一个有数据库操作的网站,可以将添加类操作写成一个COM+服务,查找类操作写成另一个COM+服务。
    在ASP.NET代码中分另与两个服务交换数据。而不要把添加类操作与查找类操作作为一个进程内的两个线程。这样最大的好处是可以使用COM+池而不是自已的线程池来控制。
    最为为线程指定那个CPU,多少百分比,最大内存最,超时终止等又是一场恶梦不是说多线程不好,多线程不适合快节奏的商业开发,当然写SQL Server 2005这样软件除外
      

  6.   

    接上
    看看这段代码,不用lock会有什么情况   class wxd
        {
            int 值=1000;
            private Object 锁= new Object();
            void lzm(int 减数)
            {
               Console.Write("/");           lock (锁) //---如果不用lock (锁)
               {
                                 if (值>= 减数)
                    {
                        Console.WriteLine("");
                        Console.WriteLine("当前值 :  " + 值);
                        Console.WriteLine("要减去数:  " + 减数);
                        值= 值- 减数;
                        Console.WriteLine("得到值 :  " + 值);
                        Console.WriteLine("------------------------------------------");
                      }          }
                    if (值< 0)
                    {
                        //在下面的  if (值>= 减数) 中
                        //只有[值>= 减数] 才运行 [值= 值- 减数]
                        //所在在单线程的代码中,不会出现[(值< 0] ,不会运行到该段代码
                        //而在多线程调用中,如果不用lock (锁),将有可能运行到此处的代码
                        
                        Console.WriteLine("******************************************");
                        Console.WriteLine("为何会得到负值");
                        Console.WriteLine("得到值 :  " + 值);
                        Console.WriteLine("************************************");
                    }
            }        public void 开始()
            {
                Random r = new Random(DateTime.Now.Second);
                         
                for (int i = 0; i < 100; i++)
                {
                    int 减数= r.Next(1, 100); //一个随机的减数
                    lzm(减数);
                }
            }
        }///////////////////////////////////////////////////////////////////
    调用
                Thread[] 线程数组= new Thread[10];
                wxd obj = new wxd();            //初始化个线程
                for (int i = 0; i < 10; i++)
                {
                    Thread t = new Thread(obj.开始);
                    线程数组[i] = t;
                }
               
                //启动这十个线程
                for (int i = 0; i < 10; i++)
                {
                    线程数组[i].Start();
                }