簡體   English   中英

ConcurrentBag <MyType> Vs List <MyType>

[英]ConcurrentBag<MyType> Vs List<MyType>

使用ConcurrentBag(Of MyType)對使用List(Of MyType)有什么好處? CB上的MSDN頁面說明了這一點

ConcurrentBag(Of T)是一個線程安全的包實現,針對同一個線程生成和使用存儲在包中的數據的場景進行了優化

那有什么好處呢? 我可以理解Concurrency命名空間中其他集合類型的優點,但是這個讓我很困惑。

在內部,ConcurrentBag使用幾個不同的列表實現,每個寫入線程一個。

你引用的那句話的意思是,當從包中讀取時,它將優先考慮為該線程創建的列表。 意思是,它會首先檢查該線程的列表,然后再冒險爭奪另一個線程的列表。

這樣,當多個線程都在讀寫時,它可以最小化鎖爭用。 當讀取線程沒有列表或其列表為空時,它必須鎖定分配給不同線程的列表。 但是,如果你有多個線程都在讀取和寫入自己的列表,那么你就不會有鎖爭用。

這里最大的優點是ConcurrentBag<T>可以安全地從多個線程訪問,而LisT<T>則不是。 如果線程安全訪問對您的場景很重要,那么像ConcurrentBag<T>這樣的類型可能優於List<T> +手動鎖定。 在我們真正回答這個問題之前,我們需要更多地了解您的情景。

另外List<T>是一個有序集合,而ConcurrentBag<T>則不是。

TLDR; 我會說局部鎖定更快但差異可以忽略不計 (或者我設置我的測試)。

績效分析:

private static IEnumerable<string> UseConcurrentBag(int count)
    {
        Func<string> getString = () => "42";

        var list = new ConcurrentBag<string>();
        Parallel.For(0, count, o => list.Add(getString()));
        return list;
    }

    private static IEnumerable<string> UseLocalLock(int count)
    {
        Func<string> getString = () => "42";
        var resultCollection = new List<string>();
        object localLockObject = new object();
        Parallel.For(0, count, () => new List<string>(), (word, state, localList) =>
        {
            localList.Add(getString());
            return localList;
        },
            (finalResult) => { lock (localLockObject) resultCollection.AddRange(finalResult); }
            );

        return resultCollection;
    }

    private static void Test()
    {
        var s = string.Empty;
        var start1 = DateTime.Now;
        var list = UseConcurrentBag(5000000);
        if (list != null)
        {
            var end1 = DateTime.Now;
            s += " 1: " + end1.Subtract(start1);
        }

        var start2 = DateTime.Now;
        var list1 = UseLocalLock(5000000);
        if (list1 != null)
        {
            var end2 = DateTime.Now;
            s += " 2: " + end2.Subtract(start2);
        }

        if (!s.Contains("sdfsd"))
        {
        }
    }

使用ConcurrentBag運行3次錯誤的邊距與5M記錄相對應

“1:00:00:00.4550455 2:00:00:00.4090409”
“1:00:00:00.4190419 2:00:00:00.4730473”
“1:00:00:00.4780478 2:00:00:00.3870387”

3運行ConcurrentBag vs Local lock with 5M records:

“1:00:00:00.5070507 2:00:00:00.3660366”
“1:00:00:00.4470447 2:00:00:00.2470247”
“1:00:00:00.4420442 2:00:00:00.2430243”

擁有50M記錄

“1:00:00:04.7354735 2:00:00:04.7554755”
“1:00:00:04.2094209 2:00:00:03.2413241”

我會說局部鎖定速度略快

更新:開啟(Xeon X5650 @ 2.67GHz 64位Win7 6核心)“本地鎖定”似乎表現更好

擁有50M記錄。

1:00:00:09.7739773 2:00:00:06.8076807
1:00:00:08.8858885 2:00:00:04.6184618
1:00:00:12.5532552 2:00:00:06.4866486

與其他並發集合不同, ConcurrentBag<T>針對單線程使用進行了優化。
List<T>不同, ConcurrentBag<T>可以同時從多個線程使用。

我認為您應該將其視為“多個線程訪問容器並且每個線程都可以生成和/或使用數據”,它絕對適用於並行場景。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM