多線程-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秒,第二個程序沒法得到該互斥量

相關文章
相關標籤/搜索