承保三次唯有二个线程访谈和退换分享数据的景况

  在线程里,要是急需共享数据,那么势一定要接受同步本事,确认保证二遍独有三个线程访谈和校订共享数据的景观。在.net中,lock语句、Interlocked类和Monitor类可用于进度之中的一路。

1、lock语句与线程安全

  lock语句是安装锁定和清除锁定的意气风发种简单方法。在应用lock语句在此之前,先踏入另一个争用条件。举个例子:

public class SharedState
{
    public int State { get; set; }
}
public class Job
{
    SharedState sharedState;
    public Job(SharedState sharedState)
    {
        this.sharedState = sharedState;
    }
    public void DoTheJob()
    {
        for (int i = 0; i < 50000; i++)
        {
                sharedState.State += 1;
        }
    }
}
static void Main()
{
    int numTasks = 20;
    var state = new SharedState();
    var tasks = new Task[numTasks];//定义20个任务

    for (int i = 0; i < numTasks; i++)
    {
        tasks[i] = Task.Run(() => new Job(state).DoTheJob());//启动20个任务,同时对数据进行修改
    }

    for (int i = 0; i < numTasks; i++)
    {
        tasks[i].Wait();//等待所有任务结束
    }

    Console.WriteLine("summarized {0}", state.State);//预想应该输出:summarized 1000000
}

  实际上的出口与预期输出并不均等,每一回运营的输出结果都不可同等对待,但不曾一个是精确的。倘诺将线程数量收缩,那么拿到正确值的次数会追加,但亦不是历次都没有错。

  使用lock关键字,能够兑现多少个线程访谈同一个数量时的一块儿难点。lock语句表示等待钦赐对象的锁定,该对象只好时引用类型。举行锁定后——只锁定了一个线程,就运转lock语句块中的代码,在lock块最终接触锁定,以便另三个线程可以锁定该目标。

lock(obj)
{
    //执行代码
}
//锁定静态成员,可以所以其类型(object)
lock(typeof(StaticCalss))
{
    //执行代码
}

  所以改善上述的代码,使用SyncRoot格局。但是,即便是对品质的拜会举办锁定:

public class SharedState
{
    private object syncRoot = new object();

    private int state = 0;
    public int State
    {
        get { lock (syncRoot) return state; }
        set { lock (syncRoot) state = value; }
    }
}

  仍会身不由己后边的争用项境。在章程调用get存款和储蓄器,以拿到state的前段时间值,然后set存款和储蓄器给state设置新值。在调用对象的get和set存款和储蓄器时期,对象并从未锁定,另叁个线程仍旧能够拿走一时值。最佳的方法是在不改过SharedState类的前提下,在调用方法中,将lock语句增加到十一分的地点:

public class SharedState
{
    public int State { get; set; }
}
public class Job
{
    SharedState sharedState;
    public Job(SharedState sharedState)
    {
        this.sharedState = sharedState;
    }
    public void DoTheJob()
    {
        for (int i = 0; i < 50000; i++)
        {
            lock (sharedState)
            {
                sharedState.State += 1;
            }
        }
    }
}

  在一个地方采取lock语句并不意味着访谈对象的任何线程都在等待。必需对各种访谈分享数据的线程展现选择同步功用。

  为使对state的改变作为二个原子操作,改善代码:

public class SharedState
{
    private int state = 0;
    public int State { get { return state; } }
    public int IncrementState()
    {
        lock (this)
        {
            return ++state;
        }
    }
}
//外部访问
public void DoTheJob()
{
    for (int i = 0; i < 50000; i++)
    {
         sharedState.IncrementState();        
    }
}

2、Interlocked类

  Interlocked类用于使变量的粗略语句原子化。i++并非线程安全的,它涉及多个步骤:取值、自增、存值。这几个操作恐怕被线程调整器打断。Interlocked类提供了以线程安全的法子依次增加、依次减少、调换和读取值的办法。Interlocked类只好用于简单的联合签名难点,况兼比很快。因而,上边的IncrementState()方法的代码能够改为:return
Interlocked.Increment(ref state);

3、Monitor类

  lcok语句最后会有C#编译器剖判为利用Monitor类。

lock(obj)
{
    //执行代码
}

  轻易的lock(obj)语句会被分析为调用Enter()方法,该方法会一贯等候,直到线程锁定目的。一回唯有二个线程能锁定目的,只要撤销锁定,线程就能够进来同步阶段。Monitor类的Exit()方法消亡锁定。编写翻译器把Exit()方法放在try块的finally中,无论是还是不是抛出至极,都将要语句块运转末尾撤消锁定。

Monitor.Enter(obj);
try
{
    //执行代码
}
finally
{
    Monitor.Exit(obj);
}

  绝对于lock语句,Mpnitor类能够安装多少个等候被锁定的超时值。这样就不会无有效期的等待锁定,假如等待锁按期间超过规定时期,则赶回false,表示未被锁定,线程不再等待,试行其余操作。也许今后,该线程会再度尝试拿到锁定:

bool lockTaken = false;
Monitor.TryEnter(obj,500, ref lockTaken);//在500ms内,是否锁定了对象
if (lockTaken)
{
    try
    {
        //执行代码
    }
    finally
    {
        Monitor.Exit(obj);
    }
}
else
{
    //未获得锁定,执行代码
}

   若是依照对象的锁定目的(Monitor卡塔 尔(英语:State of Qatar)的种类开拓由于垃圾回笼而过高,能够利用SpinLock结构。,SpinLock结构适用于:有恢宏的锁定,並且锁依时期总是充足短的景况。应幸免选用七个SpinLock结构,也无须调用任何只怕堵塞的开始和结果。

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

*
*
Website