C#多线程例(转)

来源:互联网 发布:网络寻线仪价格 编辑:程序博客网 时间:2024/05/09 21:28

 
转载自:http://blog.csdn.net/ilovemsdn/archive/2007/08/10/1735495.aspx (部分)

      多线程公用一个对象时,也会出现和公用代码类似的问题,这种问题就不应该使用lock关键字了,这里需要用到System.Threading中的一个类Monitor,我们可以称之为监视器,Monitor提供了使线程共享资源的方案。

  Monitor类可以锁定一个对象,一个线程只有得到这把锁才可以对该对象进行操作。对象锁机制保证了在可能引起混乱的情况下一个时刻只有一个线程可以访问这个对象。Monitor必须和一个具体的对象相关联,但是由于它是一个静态的类,所以不能使用它来定义对象,而且它的所有方法都是静态的,不能使用对象来引用。下面代码说明了使用Monitor锁定一个对象的情形:

        ......
  Queue oQueue
=new Queue();
   ...... 
  Monitor.Enter(oQueue);
  ......
//现在oQueue对象只能被当前线程操纵了
   Monitor.Exit(oQueue);// 释放锁 


  如上所示,当一个线程调用Monitor.Enter()方法锁定一个对象时,这个对象就归它所有了,其它线程想要访问这个对象,只有等待它使用Monitor.Exit()方法释放锁。为了保证线程最终都能释放锁,你可以把Monitor.Exit()方法写在try-catch-finally结构中的finally代码块里。对于任何一个被Monitor锁定的对象,内存中都保存着与它相关的一些信息,其一是现在持有锁的线程的引用,其二是一个预备队列,队列中保存了已经准备好获取锁的线程,其三是一个等待队列,队列中保存着当前正在等待这个对象状态改变的队列的引用。当拥有对象锁的线程准备释放锁时,它使用Monitor.Pulse()方法通知等待队列中的第一个线程,于是该线程被转移到预备队列中,当对象锁被释放时,在预备队列中的线程可以立即获得对象锁
 

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

/*** 例子来源:http://blog.csdn.net/ilovemsdn/archive/2007/08/10/1735495.aspx **/
namespace ConsoleApplication1
{
    
public class Cell            //Cell为个被操作的对象的类
    //消费者线程将调用ReadFromCell()读取cellContents的内容并且显示出来
      
//生产者进程将调用WriteToCell()方法向cellContents写入数据

        
int cellContents;        // Cell对象里边的内容
        bool readerFlag = false// 状态标志,为true时可以读取,为false则正在写入
        public int ReadFromCell()
      
{
          
lock (this// 关键字lock把一段代码定义为互斥段(critical section),
                      
//互斥段在一个时刻内只允许一个线程进入执行,而其他线程必须等待
        {
           
if (!readerFlag)//如果现在不可读取
           
               
try
                     
{  //等待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//重置readerFlag标志,表示消费行为已经完成
        Monitor.Pulse(this);//通知WriteToCell()方法(该方法在另外一个线程中执行,等待中)
    }

    
return cellContents;
  }


    
public void WriteToCell(int n)
  
{
    
lock(this)
    
{
    
if (readerFlag)
    
{
      
try
      
{
        Monitor.Wait(
this);
      }

      
catch (SynchronizationLockException e)
      
{//当同步方法(指Monitor类除Enter之外的方法)在非同步的代码区被调用
        Console.WriteLine(e);
      }

      
catch (ThreadInterruptedException e)
      
{//当线程在等待状态的时候中止 
        Console.WriteLine(e);
      }

    }

    cellContents 
= n;
    Console.WriteLine(
"Produce: {0}",cellContents);
    readerFlag 
= true
    Monitor.Pulse(
this); //通知另外一个线程中正在等待的ReadFromCell()方法
    }

      }

    }




    
public class CellProducer// 生产者线程
    {
        Cell cell; 
// 被操作的Cell对象
        int quantity = 1// 生产者生产次数,初始化为1 

        
public CellProducer(Cell box, int request)
        
{
            
//构造函数
            cell = box;
            quantity 
= request;
        }

        
public void ThreadRun()
      
{
        
for(int looper=1; looper<=quantity; looper++)
            cell.WriteToCell(looper); 
//生产者向操作对象写入信息
      }

    }


    
public class CellConsumer //消费者线程
    {
        Cell cell;
        
int quantity = 1;

        
public CellConsumer(Cell box, int request)
        
{
            cell 
= box;
            quantity 
= request;
        }

        
public void ThreadRun()
        
{
            
int valReturned;
            
for (int looper = 1; looper <= quantity; looper++)
                valReturned 
= cell.ReadFromCell();//消费者从操作对象中读取信息
        }

    }




    
public class MonitorSample
    
{
        
//创建两个线程分别作为生产者和消费者
        
//使用CellProducer.ThreadRun()方法和CellConsumer.ThreadRun()方法对同一个Cell对象进行操作
        public static void Main(String[] args)
        
{
            
int result = 0;//一个标志位,如果是0表示程序没有出错,如果是1表明有错误发生  
            Cell cell = new Cell();

            
//下面使用cell初始化CellProducer和CellConsumer两个类,生产和消费次数均为20次
            CellProducer prod = new CellProducer(cell, 20);
            CellConsumer cons 
= new CellConsumer(cell, 20);

            Thread producer 
= new Thread(new ThreadStart(prod.ThreadRun));
            Thread consumer 
= new Thread(new ThreadStart(cons.ThreadRun));
            
//生产者线程和消费者线程都已经被创建,但是没有开始执行 

            
try
            
{
                producer.Start();
                consumer.Start();
                
//谁调用Join谁被阻塞,等待被调用的线程执行完之后再执行本身.
                producer.Join();
                consumer.Join();
                Console.ReadLine();
            }

            
catch (ThreadStateException e)
            
{
               
//当线程因为所处状态的原因而不能执行被请求的操作
                Console.WriteLine(e);
                result 
= 1;
            }

            
catch (ThreadInterruptedException e)
            
{
               
//当线程在等待状态的时候中止
                Console.WriteLine(e);
                result 
= 1;
            }

            
//尽管Main()函数没有返回值,但下面这条语句可以向父进程返回执行结果
            Environment.ExitCode = result;
        }

    }
 


}

在上面的例程中,同步是通过等待Monitor.Pulse()来完成的。首先生产者生产了一个值,而同一时刻消费者处于等待状态,直到收到生产者的“脉冲(Pulse)”通知它生产已经完成,此后消费者进入消费状态,而生产者开始等待消费者完成操作后将调用Monitor.Pulese()发出的“脉冲”。它的执行结果很简单:

        Produce: 1
  Consume: 
1
  Produce: 
2
  Consume: 
2
  Produce: 
3
  Consume: 
3
  ...
  ...
  Produce: 
20
  Consume: 
20