程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> C#多線程技術總結(同步),

C#多線程技術總結(同步),

編輯:C#入門知識

C#多線程技術總結(同步),


二、串行(同步):

1.lock、Monitor--注意鎖定的對象必需是引用類型(string類型除外)

示例:

        private static object syncObject = new object();

        private static void TaskWork(object i)
        {
            Console.WriteLine("我是任務:{0}",i);
            lock (syncObject)
            {
                Thread.Sleep(1000);
                Console.WriteLine("我是任務:{0},線程ID:{1}",i,Thread.CurrentThread.ManagedThreadId);
            }

            try
            {
                Monitor.Enter(syncObject);
                Console.WriteLine("我是任務:{0},線程ID:{1}", i, Thread.CurrentThread.ManagedThreadId);
            }
            finally
            {
                Monitor.Exit(syncObject);
            }
        }

//調用
Task.Factory.StartNew(TaskWork,1);
Task.Factory.StartNew(TaskWork, 2);

2.Interlocked

示例:

            int i=1;
            Interlocked.Increment(ref i); //增量+1=2;
            Console.WriteLine("i當前的值:{0}", i);

            Interlocked.Decrement(ref i); //減量-1=0;
            Console.WriteLine("i當前的值:{0}", i);

            Interlocked.Exchange(ref i, 2);//賦值=2;
            Console.WriteLine("i當前的值:{0}",i);

            Interlocked.CompareExchange(ref i, 10, 2);//比較交換值,當i=2時,則將i賦值為10;
            Console.WriteLine("i當前的值:{0}", i);

3.Mutex--可以實現進程間的同步,甚至是兩個遠程進程間的同步

示例:

            var t1 = new Task(() =>
            {
                Console.WriteLine("我是第一個任務!");
                Mutex m = new Mutex(false, "test");
                m.WaitOne();
                Console.WriteLine("第一個任務完成!");
                m.ReleaseMutex();
            });

            var t2 = new Task(() =>
            {
                Console.WriteLine("我是第二個任務!");
                Mutex m = new Mutex(false, "test");
                m.WaitOne();
                Console.WriteLine("第二個任務完成!");
                m.ReleaseMutex();
            });

            t1.Start();
            t2.Start();

 4.ReaderWriterLock 、ReaderWriterLockSlim--如果在某一時刻資源並沒有獲取寫的獨占權,那麼可以獲得多個讀的訪問權,單個寫入的獨占權,如果某一時刻已經獲取了寫入的獨占權,那麼其它讀取的訪問權必須進行等待. 

示例:

        static ReaderWriterLock rwLock = new ReaderWriterLock();

        static void Read(object state)
        {
            Console.WriteLine("我是讀線程,線程ID是:{0}",Thread.CurrentThread.ManagedThreadId);
            rwLock.AcquireReaderLock(Timeout.Infinite);//無限期等待,需要顯式調用ReleaseReaderLock釋放鎖
            var readList = state as IEnumerable<int>;
            foreach (int item in readList)
            {
                Console.WriteLine("讀取當前的值為:{0}", item);
                Thread.Sleep(500);
            }
            Console.WriteLine("讀完成,線程ID是:{0}", Thread.CurrentThread.ManagedThreadId);
            rwLock.ReleaseReaderLock();
            
        }


        static void Write(object state)
        {
            Console.WriteLine("我是寫線程,線程ID是:{0}", Thread.CurrentThread.ManagedThreadId);
            rwLock.AcquireWriterLock(Timeout.Infinite); //無限期等待,需要顯式調用ReleaseWriterLock釋放鎖
            var writeList = state as List<int>;
            int lastCount=writeList.Count();
            for (int i = lastCount; i <= 10+lastCount; i++)
            {
                writeList.Add(i);
                Console.WriteLine("寫入當前值:{0}",i);
                Thread.Sleep(500);
            }
            Console.WriteLine("寫完成,線程ID是:{0}", Thread.CurrentThread.ManagedThreadId);
            rwLock.ReleaseWriterLock();
        }

    //調用:
            var rwList = new List<int>();

            var t1 = new Thread(Write);
            var t2 = new Thread(Read);
            var t3 = new Thread(Write);
            var t4 = new Thread(Read);
            
            t1.Start(rwList);
            t2.Start(rwList);
            t3.Start(rwList);
            t4.Start(rwList);

 

5.SynchronizationAttribute--確保某個類的實例在同一時刻只能被一個線程訪問,類的定義要求:A.類上必需標記SynchronizationAttribute特性,B.類必需繼承自System.ContextBoundObject對象

示例:

    [Synchronization(SynchronizationAttribute.REQUIRED,true)]
    public class Account : System.ContextBoundObject
    {
        private static int _balance;
        public int Blance
        {
            get
            {
                return _balance;
            }
        }

        public Account()
        {
            _balance = 1000;
        }

        public void WithDraw(string name,object money)
        {
            if ((int)money <= _balance)
            {
                Thread.Sleep(2000);
                _balance = _balance - (int)money;
                Console.WriteLine("{0} 取錢成功!余額={1}", name, _balance);
            }
            else
            {
                Console.WriteLine("{0} 取錢失敗!余額不足!", name);
            }
        }
    }

//調用:
            var account = new Account();
            Parallel.Invoke(() =>
            {
                account.WithDraw("張三",600);

            }, () =>
            {
                account.WithDraw("李四",600);
            });

6.MethodImplAttribute--使整個方法上鎖,直到方法返回,才釋放鎖

示例:

    public class Account
    {
        private static int _balance;
        public int Blance
        {
            get
            {
                return _balance;
            }
        }

        public Account()
        {
            _balance = 1000;
        }

        [MethodImpl(MethodImplOptions.Synchronized)]
        public void WithDraw(string name,object money)
        {
            if ((int)money <= _balance)
            {
                Thread.Sleep(2000);
                _balance = _balance - (int)money;
                Console.WriteLine("{0} 取錢成功!余額={1}", name, _balance);
            }
            else
            {
                Console.WriteLine("{0} 取錢失敗!余額不足!", name);
            }
        }
    }

//調用
            var account = new Account();
            Parallel.Invoke(() =>
            {
                account.WithDraw("張三",600);

            }, () =>
            {
                account.WithDraw("李四",600);
            });

7.AutoResetEvent、ManualResetEvent、ManualResetEventSlim--調用WaitOne、WaitAny或WaitAll來使線程等待事件,調用Set方法發送信號,事件將變為終止狀態,等待的線程被喚醒

示例:

            AutoResetEvent arEvent = new AutoResetEvent(false);//默認為無信號,處於非終止狀態
            Task.Factory.StartNew((o) => {
                for (int i = 1; i <= 10; i++)
                {
                    Console.WriteLine("循環第{0}次",i);
                }
                arEvent.Set();//發送信號,處於終止狀態
            },arEvent);


            arEvent.WaitOne();//等待信號,收到信號後則繼續下面的執行

            Console.WriteLine("我是主線程,我繼續執行!");
            Console.Read();

 8.Sempaphore、SemaphoreSlim(不可跨進程)--信號量,可實現線程、進程間同步

示例:

    public class WashRoom
    {
        private readonly Semaphore sem;

        public WashRoom(int maxUseableCount)
        {
            sem = new Semaphore(maxUseableCount, maxUseableCount, "WC");
        }

        public void Use(int i)
        {
            Task.Factory.StartNew(() =>
                {
                    Console.WriteLine("第{0}個人等待進入", i);
                    // WaitOne:如果還有“空位”,則占位,如果沒有空位,則等待;
                    sem.WaitOne();
                    Console.WriteLine("第{0}個人成功進入,使用中", i);
                    // 模擬線程執行了一些操作
                    Thread.Sleep(100);
                    Console.WriteLine("第{0}個人用完,離開了", i);
                    // Release:釋放一個“空位”
                    sem.Release();
                });
        }
    }

//調用:
            var wc = new WashRoom(5);
            for (int i = 1; i <= 7; i++)
            {
                wc.Use(i);
            }

9.Barrier--屏障,使多個任務能夠采用並行方式依據某種算法在多個階段中協同工作,即:將一個階段的事情分成多個線程來異步執行,執行完畢後再同時進入下一個階段

示例:

            int taskSize = 5;
            Barrier barrier = new Barrier(taskSize, (b) =>
            {
                Console.WriteLine(string.Format("{0}當前階段編號:{1}{0}", "-".PadRight(15, '-'), b.CurrentPhaseNumber));
            });

            var tasks = new Task[taskSize];

            for (int i = 0; i < taskSize; i++)
            {
                tasks[i] = Task.Factory.StartNew((n) =>
                {
                    Console.WriteLine("Task : #{0}   ---->  處理了第一部份數據。", n);
                    barrier.SignalAndWait();

                    Console.WriteLine("Task : #{0}   ---->  處理了第二部份數據。", n);
                    barrier.SignalAndWait();

                    Console.WriteLine("Task : #{0}   ---->  處理了第三部份數據。", n);
                    barrier.SignalAndWait();

                }, i);
            }

            Task.WaitAll(tasks);

10.SpinLock--自旋鎖,僅限鎖定的時間較短

示例:

            SpinLock sLock = new SpinLock();
            int num = 0;
            Action action = () =>
            {
                bool lockTaken = false;
                for (int i = 0; i < 10; i++)
                {
                    lockTaken = false;
                    try
                    {
                        sLock.Enter(ref lockTaken);
                        Console.WriteLine("{0}+1={1} ---線程ID:[{2}]", num, ++num,Thread.CurrentThread.ManagedThreadId);
                        Thread.Sleep(new Random().Next(9));
                    }
                    finally
                    {
                        //真正獲取之後,才釋放
                        if (lockTaken) sLock.Exit();
                    }
                }
            };

//多線程調用:
            Parallel.Invoke(action, action, action);
            Console.WriteLine("合計:{0}", num);

11.SpinWait--自旋等待,輕量級

            Thread.Sleep(1000);//線程等待1S;
            Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"));

            SpinWait.SpinUntil(() => false, 1000);//自旋等待1S
            Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"));

            Thread.SpinWait(100000);//指定CPU的循環次數,時間間隔處決於處理器的運行速度,一般不建議使用
            Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"));

12.CountdownEvent--與Sempaphore功能類似,但CountdownEvent支持動態調整信號計數

 示例:

        static void TimeLimitShopping(int custCount,int times,CountdownEvent countdown)
        {
            var customers = Enumerable.Range(1, custCount);
            foreach (var customer in customers)
            {
                int currentCustomer = customer;
               Task.Factory.StartNew(()=>
                {
                    SpinWait.SpinUntil(() => false, 1000);
                    Console.WriteLine("第{0}波客戶購買情況:Customer-{1}-已購買.", times, currentCustomer);
                    countdown.Signal();
                });
                //countdown.AddCount();
            }
        }


//調用:
var countdown = new CountdownEvent(5);
                TimeLimitShopping(5, 1, countdown);
                countdown.Wait();


                countdown.Reset(10);
                TimeLimitShopping(10, 2, countdown);
                countdown.Wait();

                countdown.Reset(20);
                TimeLimitShopping(20, 3, countdown);
                countdown.Wait();

 最後分享在System.Collections.Concurrent命名空間下的幾個並發集合類:

ConcurrentBag<T>:表示線程安全的無序集合;

ConcurrentDictionary<T>:表示線程安全的多個鍵值對集合;

ConcurrentQueue<T>:表示線程安全的先進先出集合;

ConcurrentStack<T>:表示線程安全的後進先出集合;

線程的幾個狀態(以下圖片來源於這篇文章:http://www.cnblogs.com/edisonchou/p/4848131.html):

參考以下相關文章:

歸納一下:C#線程同步的幾種方法

C#編程總結(三)線程同步

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved