您的位置:首页 > 其它

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

2012-10-05 14:59 148 查看
上一节主要讲了创建调用有参(多参)函数的线程和线程池的一些内容,这一节主要讲线程的同步。

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

下面先看一个例子:

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里执行if(biaoJi)时为biaoJi值为true,所以可以输出两句话。

出现第二句话的原因在于,两个线程同时访问biaoJi值,此时biaoJi值为发生变化,仍未false,故在执行TestShow1时biaoJi值仍为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方式: lock(obj)

     {

       //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 obj,Timespan time)来设置等待获得锁定的最长时间

使用示例:

if(Monitor.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类还有一些其他的方法:比如:

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

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

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

今天到这里基本上就这些了,当然这些都是比较常用的解决线程同步的方法,还有其他的一些方法比如AutoResetEvent 、ManualResetEvent 、 EventWaitHandle ,还有一个volatile关键字的同步方法,但是它只能在变量一级做同步。关于后边的上边所列的同步方法不常用,所以就不再做介绍了,有兴趣的朋友可以自己去了解下。希望可以对大家有所帮助。下一节会讲一下异步操作。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: