多线程-2(线程同步)

 

带着问题去思考!你们好。
今天咱们来了解下什么是线程同步?安全

首先咱们先知道这些概念和一些类;多线程

  • 执行基本的原子性
  • Mutex类
  • SemaphoreSlim类
  • AutoResetEvent类
  • ManualRestEventSlim类
  • CountDownEvent类
  • Barrier类
  • ReaderWriterLockSilm类
  • SpinWait类

咱们都知道确保当一个线程使用某些资源的时候,同时其余线程没法使用该资源。这引入一个概念是共享资源。ide

多个线程同时使用共享对象会形成不少问题。同步线程使得对共享对象的操做可以以正确的顺序执行是很是重要的。spa

首先经过一个加减例子了解下lock处理操作系统

 public abstract class CounterBase
    {
        public abstract void Increment();
        public abstract void Decrement();
    }

public class Counter:CounterBase
    {
        public int Count { get;private set; }

        public override void Decrement()
        {
            Count++;
        }

        public override void Increment()
        {
            Count--;
        }
    }
public class CounterLock : CounterBase
    {
        private readonly object _synclock = new object();
        public int Count { get; private set; }

        public override void Decrement()
        {
            lock(_synclock)
            {
                Count++;
            }
           
        }

        public override void Increment()
        {
            lock (_synclock)
            {
                Count--;
            }
        }
       
    }

 static void Main(string[] args)
        {
            #region 多线程锁
            Console.WriteLine("Incorrect counter");
            var c = new Counter();

            var t1 = new Thread(() => TestCount(c));
            var t2= new Thread(() => TestCount(c));
            var t3 = new Thread(() => TestCount(c));
            t1.Start();
            t2.Start();
            t3.Start();
            t1.Join();
            t2.Join();
            t3.Join();
            Console.WriteLine("Total count:{0}",c.Count);
            Console.WriteLine("-----------");
            Console.WriteLine("Correct counter");
            var c1 = new CounterLock();

             t1 = new Thread(() => TestCount(c1));
             t2 = new Thread(() => TestCount(c1));
             t3 = new Thread(() => TestCount(c1));
            t1.Start();
            t2.Start();
            t3.Start();
            t1.Join();
            t2.Join();
            t3.Join();
            Console.WriteLine("Total count:{0}", c1.Count);
            #endregion
        }
 static void TestCount(CounterBase c)
        {
            for (int i = 0; i < 1000; i++)
            {
                c.Increment();
                c.Decrement();
            }
        }
View Code

 

 

 

 

咱们知道,最终结果应该是0;线程

由于第一个线程获得count为10增长为11,第二个线程获得的值是11并增长为12.第一个线程获得count为12,可是递减操做发生前,第二个线程获得的值也是12,。而后第一个线程将12递减11并保存count中,同时第二个线程进行了一样的操做,结果咱们进行了两次递增操做可是只有一次递减操做。这是竞争条件(race condition)code

1:请尽可能避免使用共享对象对象

2:必须是共享的状态时候,使用原子操做。一个操做只占一个量子的时间,一次完成。这说明只有当前操做完成后,其余线程才能执行其余操做,避免死锁,和使用锁blog

3:使用不一样方式来协调线程资源

  • 将等待的线程置于阻塞状态。当线程阻塞状态,只会占用少许CPU时间,意味将引入至少一次所谓的上下文切换(context switch上下文切换是操做系统的线程调度器).该调度器会保存等待的线程等待,并切换到另外一个线程,依次恢复等待的线程的状态。这须要消耗不少资源,可是若是线程要被挂起很长时间的话,这是值得的---内核模式(kernel-mode)
  • 线程只须要等待一小段时间,不用将线程切换到阻塞状态。虽然线程等待时会浪费CPU时间,但节省了上下文切换耗费的CPU时间----用户模式(user-mode)
  • 先尝试使用用户模式等待,若是线程等待足够长时间。则会切换到阻塞状态节省CPU资源---混合模式(hybrid)

执行基本的原子操做

不用阻塞线程就可避免竞争条件

public class CounterLock : CounterBase
    {
        public int Count { get; private set; }
        public int _count;
        public override void Decrement()
        {
            
            Interlocked.Decrement(ref _count);
        }

        public override void Increment()
        {
            Interlocked.Increment(ref _count);
        }
       
    }

咱们修改CounterLock,再来看看结果

 

 咱们可能会获得0,可是最终会获得一些不肯定的非0.第一个例子是线程不安全的。第二个例子中,咱们借助Interlocked类,无需锁定任何对象便可获取正确结果。Interlocked提供了Increment.Decrement和Add基于数学操做的原子方法,编写Counter类时无需使用锁、

 Mutex类

const string MutexName = "CSharpThreadingCookbook";
        static void Main(string[] args)
        {
            using (var m=new Mutex(false,MutexName))
            {
                if(!m.WaitOne(TimeSpan.FromSeconds(5),false))
                {
                    Console.WriteLine("Second instance is running!");
                }
                else
                {
                    Console.WriteLine("Running!");
                    Console.ReadLine();
                    m.ReleaseMutex();
                }
            }
        }

 

 

程序启动,定义一个指定名称的互斥量,设置initialOwner标志为false,这意味着若是互斥量已经被建立,则容许程序获取该互斥量。若是没有得到互斥量,程序则简单显示Running

在运行一样一个程序,则会在5秒种内尝试得到互斥量,若是此时在第一个程序中按下任意键,第二个程序则会开始执行。然而若是保持等待5秒,第二个程序没法得到该互斥量

相关文章
相关标签/搜索