星星之火

燎原之势不可挡
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

线程实用解析---------(三)线程的同步

Posted on 2012-10-05 14:59  星星之火116  阅读(514)  评论(0编辑  收藏  举报

     上一节主要讲了创建调用有参(多参)函数的线程和线程池的一些内容,这一节主要讲线程的同步。

     多线程的出现解决了吞吐量和响应速度的问题,但同时也带来了资源共享问题,如死锁和资源争用。为单个资源分配多个线程可能会导致同步问题。何为线程同步呢?所谓同步,是指多个线程之间存在先后执行顺序的关联关系。如果一个线程必须在另一个线程完成某个工作后才能继续执行,则必须考虑如何让其他保持同步,以确保在系统上同时运行多个线程而不会出现死锁或逻辑错误。

下面先看一个例子:

 

class Program

    {

        static void Main(string[] args)

        {

            Thread thread = new Thread(new ThreadStart(TestShow));

            Thread thread1 = new Thread(new ThreadStart(TestShow1));

            thread.Start();

            thread1.Start();

            Console.ReadKey();

        }

        static bool biaoJi = false;

        static void TestShow()

        {

            if (!biaoJi)

            {

                biaoJi = true;

                Console.WriteLine("标记为False");               

            }

        }

        static void TestShow1()

        {

            if (biaoJi)

            {  Console.WriteLine("标记为True");}

        }

这个程序很简单,就是创建两个线程,分别调用两个函数输出一句话,两个函数都共用到了一个全局变量biaoJi,在TestShow()里边将biaoJi的值改为False,运行之后的结果如下:

 

或者

出现第一种结果原因,我们在TestShow里将biaoJi值改为true,在TestShow1里执行ifbiaoJi)时为biaoJi值为true,所以可以输出两句话。

出现第二句话的原因在于,两个线程同时访问biaoJi值,此时biaoJi值为发生变化,仍未false,故在执行TestShow1biaoJi值仍为false,所以只能输出一句话

这里就出现了线程的同步问题,结果一是我们想要的结果,但是在程序的运行过程中,往往会出现像结果二那样的结果。

如果解决这样的问题呢?为了解决这些问题,System.Threading命名空间提供了多个用于同步线程的类。这些类包括Mutex,Monitor,Interlocked,AutoResetEvent. 下面会逐步介绍一些解决同步的方法。

一、最简单也是最常用的方法,C#提供的Lock方法

Lock关键字能确保当一个线程位于代码的临界区时,另一个线程不进入临界区。如果其他线程试图进入锁定的代码段,则它将一直等待,直到锁定的对象被释放以后才能进入临界区。

下面演示下如何使用Lock

代码如下:

     Private static readonly object obj=new object();

static void TestShow()

        {

           Lock(obj)

          {

            if (!biaoJi)

            {

                biaoJi = true;

                Console.WriteLine("标记为False");    

            }

      }

        }

这样不管怎么调用显示的结果永远都是上例所示的第一种结果。

Lock使用方法:

 Lock键字将语句块标记为临界区,方法是获取给定对象的互斥锁,执行语句,然后释放该锁。 此语句的形式如下:

Object obj = new Object();lock (obj){    // code section.}

Lock注意事项:

最佳做法是定义 private 对象来锁定或 private static 对象变量来保护所有实例所共有的数据,锁定的对象不能为空

二、Monitor

Lock语句经过编译器解析为Monitor类。Monitor类的效果和Lock基本是一样的。

可以通过以下方式实现同步:

Lock方式: lockobj)

     {

       //code section

            }

Monitor类(为静态类):   Monitor.Enter(obj);

                         //code section

                         //........

                         Monitor.Exit(obj);

示例:

     static void TestShow()

        {

            Monitor.Enter(obj);

                if (!biaoJi)

                {

                    biaoJi = true;

                    Console.WriteLine("标记为False");

                }

                Monitor.Exit(obj);

        }

除此之外Monitor还有一个优点就是可以设置一个等待获得锁定的超时值,用以避免无限期的锁定。通过Monitor.TryEnter(object objTimespan time)来设置等待获得锁定的最长时间

使用示例:

ifMonitor.TryEnter(obj,1000){try  { ........... }finally { //当时间超过1秒的时候,线程不再等待 Monitor.Exit(obj); }实例:

static void TestShow()

        {

            if (Monitor.TryEnter(obj, 2000))

            {

                try

                {

                    if (!biaoJi)

                    {

                        Thread.Sleep(3000);

                        biaoJi = true;

                        Console.WriteLine("标记为False");

                    }

                }

                catch { Monitor.Exit(obj); }  }

        }

这样因为线程在修改biaoJi值时,休眠的3秒,超出了Monitor设置的等待时间,所以另一个线程已经开始执行了,执行时在函数TestShow1()中,biaoJi的值仍为false所以只输出了一句话。等到2个线程都执行完毕时,此时biaoJi的值为True

三、Mutex

Mutex的功能和C# 中的Lock一样,不同的是它可以跨进程在操作系统中,许多线程常常需要共享资源,而这些资源往往要求一次只能为一个线程服务,这种排他性地使用共享资源称为线程间的互斥。线程互斥实质上也是同步,可以看做一种特殊的线程同步。但是,进入和释放一个Mutex要花费几毫秒,效率会比较低

通常我们会使用一个Mutex的实例,调用WaitOne方法来获取锁,ReleaseMutex方法来释放锁

方法如下:

Mutex m = new Mutex();

  m.WaitOne();

//code section

//...

m.ReleaseMutex();

此外我们还可以为WaitOne()函数设置参数,以防止无限期的等待。

Mutex类还有一些其他的方法:比如:

MutexWaitAll()函数//等待所有的线程操作

MutexWaitAny()函数//多个操作时,等待指定的某个线程操作

但是在操作结束后,一定要分别进行释放。

 

今天到这里基本上就这些了,当然这些都是比较常用的解决线程同步的方法,还有其他的一些方法比如AutoResetEvent ManualResetEvent  EventWaitHandle ,还有一个volatile关键字的同步方法,但是它只能在变量一级做同步。关于后边的上边所列的同步方法不常用,所以就不再做介绍了,有兴趣的朋友可以自己去了解下。希望可以对大家有所帮助。下一节会讲一下异步操作。