C#的多线程机制初探(5)

三.线程的同步和通讯--生产者和消费者

  假设这样一种情况,两个线程同时维护一个队列,如果一个线程对队列中添加元素,而另外一个线程从队列中取用元素,那么我们称添加元素的线程为生产者,称取用元素的线程为消费者。生产者与消费者问题看起来很简单,但是却是多线程应用中一个必须解决的问题,它涉及到线程之间的同步和通讯问题。

  前面说过,每个线程都有自己的资源,但是代码区是共享的,即每个线程都可以执行相同的函数。但是多线程环境下,可能带来的问题就是几个线程同时执行一个函数,导致数据的混乱,产生不可预料的结果,因此我们必须避免这种情况的发生。C#提供了一个关键字lock,它可以把一段代码定义为互斥段(critical section),互斥段在一个时刻内只允许一个线程进入执行,而其他线程必须等待。在C#中,关键字lock定义如下:

  lock(expression) statement_block
expression代表你希望跟踪的对象,通常是对象引用。一般地,如果你想保护一个类的实例,你可以使用this;如果你希望保护一个静态变量(如互斥代码段在一个静态方法内部),一般使用类名就可以了。而statement_block就是互斥段的代码,这段代码在一个时刻内只可能被一个线程执行。

  下面是一个使用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)
      {
        //如果balance小于0则抛出异常
        throw new Exception("Negative Balance");
      }
      //下面的代码保证在当前线程修改balance的值完成之前
      //不会有其他线程也执行这段代码来修改balance的值
      //因此,balance的值是不可能小于0的
      lock (this)
      {
        Console.WriteLine("Current Thread:"+Thread.CurrentThread.Name);
        //如果没有lock关键字的保护,那么可能在执行完if的条件判断之后
        //另外一个线程却执行了balance=balance-amount修改了balance的值
        //而这个修改对这个线程是不可见的,所以可能导致这时if的条件已经不成立了
        //但是,这个线程却继续执行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();
    }
  }

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

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值