C#關於List的線程安全問題(二)

上一期,C#關於List的線程安全問題(一)我們給出了一個線程不安全的例子。

這個例子給人的感覺就是總覺得哪裏不對,命名插入5000個數據到List中,結果卻並不是自己想要的。

明明一共插入了1300個數據,結果也不是。

這都是因爲List默認線程不安全導致的,也就是當某一個線程正在往List中插入數據,結果由於其他線程也正在做插入動作,導致衝突,插入可能失敗,並且插入的順序是不可控的,除非我們並不關心插入的順序。

那麼我們怎麼樣才能讓結果是我們所需要的呢?

這裏我們引入ICollection接口(List<T>是擁有ICollection接口的),這個接口當中有個叫SyncRoot的屬性,它是個輔助屬性,它是微軟實現爲我們準備好了拿來做同步用的,也就是拿來做線程安全用的。它一般用於lock語句塊。用來將集合資源鎖定,可用於同步對ICollection的訪問的對象。

public object SyncRoot { get; }

它是一個實例化的屬性,即每個List實例對象都會有各自的一個SyncRoot。如果想安全訪問集合對象,我們可以如下使用:

ICollection myCollection = someCollection;
lock(myCollection.SyncRoot)
{
    // Some operation on the collection, which is now thread safe.
}

所以C#關於List的線程安全問題(一)中的例子,我們可以稍加修改如下:

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using System.Collections;

namespace vscode_test2
{
    class Program
    {
        static void Main(string[] args)
        {
            List<int> mylist = new List<int>();
            var t = Task.Run(()=>{
                Thread.Sleep(2000);
                lock((mylist as ICollection).SyncRoot)
                {
                    for(int i=0; i<8000; i++)
                    {
                        mylist.Add(3);
                        Thread.Sleep(1);
                    }
                    System.Console.WriteLine($"task: list size:{mylist.Count}");
                }
            });
            Thread.Sleep(2000);
            lock((mylist as ICollection).SyncRoot)
            {
                for(int i=0; i<5000; i++)
                {
                    Thread.Sleep(1);
                    mylist.Add(6);
                }
                System.Console.WriteLine($"main: list size:{mylist.Count}");
            }
            t.Wait();
            Console.Read();
        }
    }
}

/*
main: list size:5000
task: list size:13000
*/

這是輸出結果,就是我們想要的。當然這個想要的,是如您業務所願。如果你是無所謂插入順序或者插入是否失敗,那麼代碼隨你寫。因此,安不安全,同步與否是需要有業務邏輯來定的。反正,如果我們想同步,那麼微軟已經爲我們準備了一個叫做SyncRoot屬性,我們不需要爲了同步某個Collection集合額外定義一個鎖對象,集合對象內部就已經替我們準備了一把叫做SyncRoot的鎖。

同樣的,其他的Collection類,也可以用這個方式實現線程安全。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章