थ्रेड-सुरक्षित सूची <T> संपत्ति


122

मैं List<T>एक संपत्ति के रूप में एक कार्यान्वयन चाहता हूं जिसे बिना किसी संदेह के धागा-सुरक्षित रूप से उपयोग किया जा सके।

कुछ इस तरह:

private List<T> _list;

private List<T> MyT
{
    get { // return a copy of _list; }
    set { _list = value; }
}

ऐसा लगता है कि अभी भी मुझे संग्रह की एक प्रति (क्लोन) वापस करने की आवश्यकता है, इसलिए यदि कहीं हम संग्रह को पुनरावृत्त कर रहे हैं और उसी समय संग्रह सेट है, तो कोई अपवाद नहीं उठाया गया है।

थ्रेड-सुरक्षित संग्रह संपत्ति कैसे लागू करें?


4
ताले का उपयोग करें, कि यह करना चाहिए।
atoMerz

IList<T>(बनाम List<T>) के थ्रेड-सुरक्षित कार्यान्वयन का उपयोग कर सकते हैं ?
ग्रेग


ब्लॉकिंगकॉलेक्शन या
कॉन्ट्रैक्चुअल छाया का

संपत्ति के पीछे वस्तु के साथ आपको क्या संचालन करने की आवश्यकता है? क्या यह संभव है कि आपको उन सभी चीजों की आवश्यकता नहीं है जो List<T>लागू होती हैं? यदि हाँ, तो क्या आप कृपया एक इंटरफ़ेस प्रदान कर सकते हैं जो आपको List<T>पहले से मौजूद हर चीज के बारे में पूछने के बजाय चाहिए?
विक्टर यारेमा

जवाबों:


185

यदि आप .Net 4 को लक्षित कर रहे हैं, तो System.Collections.Concurrent Namespace में कुछ विकल्प हैं

आप ConcurrentBag<T>इस मामले में इसके बजाय उपयोग कर सकते हैंList<T>


5
सूची की तरह <T> और शब्दकोश के विपरीत, समवर्तीबाग डुप्लिकेट को स्वीकार करता है।
द लाइट

115
ConcurrentBagअनियंत्रित संग्रह है, इसलिए इसके विपरीत List<T>यह आदेश देने की गारंटी नहीं देता है। इसके अलावा आप अनुक्रमणिका द्वारा आइटम तक नहीं पहुँच सकते
राडेक स्ट्रॉम्स्की

11
@ RadekStromský सही है, और मामले में जब आप एक आदेशित समवर्ती सूची चाहते हैं, तो आप समवर्ती queue (FIFO) या कोशिश कर सकते हैं समवर्ती (LIFO)
कैओ कान्हा


12
ConcurrentBag IList लागू नहीं करता है और वास्तव में सूची का सुरक्षित संस्करण थ्रेड नहीं है
Vasyl Zvarydchuk

87

यहां तक ​​कि इसे सबसे अधिक वोट मिले, आमतौर पर System.Collections.Concurrent.ConcurrentBag<T>एक थ्रेड-सेफ रिप्लेसमेंट के System.Collections.Generic.List<T>रूप में नहीं लिया जा सकता क्योंकि यह (Radek Stromský पहले से ही इसे इंगित करता है) का आदेश नहीं दिया गया।

लेकिन एक ऐसा वर्ग है जिसे System.Collections.Generic.SynchronizedCollection<T>फ्रेमवर्क के .NET 3.0 भाग के बाद से पहले से ही मौजूद है, लेकिन यह अच्छी तरह से उस स्थान पर छिपा हुआ है जहां कोई यह उम्मीद नहीं करता है कि यह बहुत कम ज्ञात है और शायद आपने कभी भी इस पर ठोकर नहीं खाई है (कम से कम मैंने कभी नहीं किया)।

SynchronizedCollection<T>विधानसभा System.ServiceModel.dll में संकलित किया गया है (जो क्लाइंट प्रोफाइल का हिस्सा है लेकिन पोर्टेबल क्लास लाइब्रेरी का नहीं)।

उम्मीद है की वो मदद करदे।


3
मैं रोता हूं कि यह कोर लिब में नहीं है: {एक साधारण सिंक्रोनाइज़्ड कलेक्शन अक्सर होता है, जिसकी जरूरत होती है।
user2864740

इस विकल्प की अतिरिक्त उपयोगी चर्चा: stackoverflow.com/a/4655236/12484
जॉन श्नाइडर

2
यह अच्छी तरह से छिपा हुआ है क्योंकि सिस्टम में कक्षाओं के पक्ष में पदावनत किया गया है।
डेन्फ्रोमुफा

3
और
इननेट

2
@denfromufa ऐसा लगता है कि वे इसे .net core 2.0 डॉक्स.microsoft.com/en-gb/dotnet/api/… में जोड़ते हैं
Cirelli94

17

मुझे लगता है कि एक नमूना बनाना थ्रेडसेफ़िस्ट क्लास आसान होगा:

public class ThreadSafeList<T> : IList<T>
{
    protected List<T> _interalList = new List<T>();

    // Other Elements of IList implementation

    public IEnumerator<T> GetEnumerator()
    {
        return Clone().GetEnumerator();
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return Clone().GetEnumerator();
    }

    protected static object _lock = new object();

    public List<T> Clone()
    {
        List<T> newList = new List<T>();

        lock (_lock)
        {
            _interalList.ForEach(x => newList.Add(x));
        }

        return newList;
    }
}

आप बस एक एन्यूमरेटर का अनुरोध करने से पहले सूची को क्लोन कर लेते हैं, और इस प्रकार कोई भी एन्यूमरेशन एक कॉपी से काम कर रहा है जिसे रन करते समय संशोधित नहीं किया जा सकता है।


1
क्या यह उथला क्लोन नहीं है? यदि Tएक संदर्भ प्रकार है, तो क्या यह सभी मूल वस्तुओं के संदर्भ वाली एक नई सूची नहीं लौटाएगा? यदि ऐसा है, तो यह दृष्टिकोण अभी भी थ्रेडिंग समस्या पैदा कर सकता है क्योंकि सूची की विभिन्न "प्रतियां" के माध्यम से सूची ऑब्जेक्ट्स को कई थ्रेड्स द्वारा एक्सेस किया जा सकता है।
जोएल बी

3
सही है, यह एक उथली प्रति है। बिंदु बस एक क्लोन सेट था जो अधिक से अधिक पुनरावृत्त करने के लिए सुरक्षित होगा (इसलिए newListइसमें कोई भी आइटम नहीं जोड़ा गया है या हटा दिया गया है जो गणनाकर्ता को अमान्य कर देगा)।
तीज

7
क्या _लॉक स्थिर होना चाहिए?
माइक वार्ड

4
एक और विचार। क्या यह कार्यान्वयन कई लेखकों के लिए सूत्र है? यदि नहीं, तो शायद इसे ReadSafeList कहा जाना चाहिए।
माइक वार्ड

5
@ माइक - मुझे नहीं लगता कि यह होना चाहिए, सभी उदाहरण लॉक हो जाएगा जब किसी भी उदाहरण को क्लोन किया जा रहा है!
जोश एम।

11

यहां तक ​​कि स्वीकृत जवाब है समवर्तीबाग, मुझे नहीं लगता कि यह सभी मामलों में सूची का वास्तविक प्रतिस्थापन है, क्योंकि उत्तर में राडेक की टिप्पणी कहती है: "समवर्तीबाग अनियंत्रित संग्रह है, इसलिए सूची के विपरीत यह आदेश देने की गारंटी नहीं देता है। "।

इसलिए यदि आप .NET 4.0 या उच्चतर का उपयोग करते हैं, तो सरणी इंडेक्स और TValue के साथ सरणी मान के रूप में पूर्णांक TKey के साथ समवर्ती छाया का उपयोग करने के लिए एक समाधान हो सकता है । यह प्लुरलिट्स सी # समवर्ती संग्रह पाठ्यक्रम में सूची को बदलने की सिफारिश की गई है । समवर्ती छायाकरण उपरोक्त वर्णित दोनों समस्याओं को हल करता है: इंडेक्स एक्सेसिंग और ऑर्डरिंग (हम ऑर्डर करने पर भरोसा नहीं कर सकते क्योंकि यह हुड के नीचे हैश टेबल है, लेकिन वर्तमान .NET कार्यान्वयन तत्वों को जोड़ने का क्रम बचाता है)।


1
कृपया 1 के लिए कारण प्रदान करें
tytyryty 19

मैंने वोट नहीं डाला और इसका कोई कारण नहीं है IMO। आप सही हैं लेकिन अवधारणा पहले से ही कुछ उत्तरों में उल्लिखित है। मेरे लिए, बिंदु था। .NET 4.0 में एक नया थ्रेड-सुरक्षित संग्रह था जिसके बारे में मुझे जानकारी नहीं थी। यकीन नहीं है कि स्थिति के लिए बैग या संग्रह का उपयोग किया गया है। +1
Xaqron

2
इस उत्तर में कई समस्याएं हैं: 1) ConcurrentDictionaryएक शब्दकोष है, सूची नहीं। 2) यह अपने स्वयं के उत्तर राज्यों के रूप में, पूर्व निर्धारित आदेश की गारंटी नहीं है, जो उत्तर पोस्ट करने के लिए आपके घोषित कारण का खंडन करता है। 3) यह इस उत्तर में प्रासंगिक उद्धरण लाए बिना एक वीडियो के लिए लिंक करता है (जो कि वैसे भी उनके लाइसेंस के साथ सहमति में नहीं हो सकता है)।
jpmc26

यदि आप current implementationस्पष्ट रूप से प्रलेखन द्वारा इसकी गारंटी नहीं दी जाती है तो आप चीजों पर भरोसा नहीं कर सकते । सूचना के बिना किसी भी समय कार्यान्वयन बदल सकता है।
विक्टर यारमा

@ jpmc26, हाँ यह निश्चित रूप से सूची के लिए एक पूर्ण प्रतिस्थापन नहीं है, हालाँकि यह एक स्वीकृत उत्तर के रूप में समवर्तीबाग के साथ है - यह सूची का सख्त प्रतिस्थापन नहीं है लेकिन चारों ओर काम करता है। अपनी चिंताओं का जवाब देने के लिए: 1) समवर्ती छायांकन एक ऐसा शब्दकोश है जो आपके द्वारा सही नहीं की गई सूची है, हालाँकि सूची में पीछे सरणी है, जो O (1) में अनुक्रमित कर सकता है (जैसे कि कुंजी 2 के रूप में int के साथ शब्दकोश) हां आदेश डॉक द्वारा गारंटी नहीं है ( भले ही यह संरक्षित है), लेकिन स्वीकार किए गए
समवर्तीबाग

9

C # की ArrayListकक्षा में एक Synchronizedविधि है।

var threadSafeArrayList = ArrayList.Synchronized(new ArrayList());

यह किसी भी उदाहरण के आसपास एक थ्रेड सुरक्षित आवरण देता है IList। धागा सुरक्षा सुनिश्चित करने के लिए सभी ऑपरेशनों को आवरण के माध्यम से किया जाना चाहिए।


1
किस भाषा के बारे मे बात कर रहे हो?
जॉन डेमेट्रियौ

जावा? कुछ विशेषताओं में से एक जो मुझे इसके बारे में याद आती है। लेकिन यह आमतौर पर इस प्रकार लिखा जाता है: Collections.synchronizedList (new ArrayList ());
निक

2
यह मान्य है C # मान लें कि आपके पास एक System.Collections है या आप var System.Collections.ArrayList.Synchronized (new System.Collections.ArrayList ()) का उपयोग कर सकते हैं;
user2163234

5

यदि आप T ( https://referencesource.microsoft.com/#mscorlib/system/collections/generic/list.cs,c66df6f36c131877 ) की सूची के स्रोत कोड को देखते हैं, तो आप देखेंगे कि वहाँ एक वर्ग है (जो निश्चित रूप से है) आंतरिक - क्यों, Microsoft, क्यों?!?) को T का सिंक्रोनाइज़्डलिस्ट कहा जाता है। मैं यहाँ कोड चिपकाते हुए कॉपी हूँ:

   [Serializable()]
    internal class SynchronizedList : IList<T> {
        private List<T> _list;
        private Object _root;

        internal SynchronizedList(List<T> list) {
            _list = list;
            _root = ((System.Collections.ICollection)list).SyncRoot;
        }

        public int Count {
            get {
                lock (_root) { 
                    return _list.Count; 
                }
            }
        }

        public bool IsReadOnly {
            get {
                return ((ICollection<T>)_list).IsReadOnly;
            }
        }

        public void Add(T item) {
            lock (_root) { 
                _list.Add(item); 
            }
        }

        public void Clear() {
            lock (_root) { 
                _list.Clear(); 
            }
        }

        public bool Contains(T item) {
            lock (_root) { 
                return _list.Contains(item);
            }
        }

        public void CopyTo(T[] array, int arrayIndex) {
            lock (_root) { 
                _list.CopyTo(array, arrayIndex);
            }
        }

        public bool Remove(T item) {
            lock (_root) { 
                return _list.Remove(item);
            }
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
            lock (_root) { 
                return _list.GetEnumerator();
            }
        }

        IEnumerator<T> IEnumerable<T>.GetEnumerator() {
            lock (_root) { 
                return ((IEnumerable<T>)_list).GetEnumerator();
            }
        }

        public T this[int index] {
            get {
                lock(_root) {
                    return _list[index];
                }
            }
            set {
                lock(_root) {
                    _list[index] = value;
                }
            }
        }

        public int IndexOf(T item) {
            lock (_root) {
                return _list.IndexOf(item);
            }
        }

        public void Insert(int index, T item) {
            lock (_root) {
                _list.Insert(index, item);
            }
        }

        public void RemoveAt(int index) {
            lock (_root) {
                _list.RemoveAt(index);
            }
        }
    }

व्यक्तिगत रूप से मुझे लगता है कि वे जानते थे कि सेमाफोरस्लीम का उपयोग करके एक बेहतर कार्यान्वयन बनाया जा सकता है, लेकिन यह नहीं मिला।


2
+1 _rootप्रत्येक एक्सेस (पढ़ने / लिखने) में पूरे संग्रह ( ) को लॉक करना एक धीमा समाधान बनाता है। शायद इस वर्ग के लिए आंतरिक बने रहना बेहतर है।
Xaqron

3
यह कार्यान्वयन थ्रेड-सुरक्षित नहीं है। यह अभी भी "System.InvalidOperationException: फेंकता है: 'संग्रह को संशोधित किया गया था; एन्यूमरेशन ऑपरेशन निष्पादित नहीं हो सकता है।"
रमण ज़िलाइच

2
यह थ्रेड सुरक्षा से संबंधित नहीं है, लेकिन इस तथ्य से कि आप संग्रह को बदल रहे हैं और बदल रहे हैं। जब सूची बदली गई तो अपवाद को एन्यूमरेटर द्वारा फेंका गया है। इसके चारों ओर पाने के लिए आपको अपने स्वयं के IEnumerator को लागू करने या कोड को बदलने की आवश्यकता है ताकि यह एक ही समय में एक ही संग्रह को पुनरावृत्त और परिवर्तित न करे।
सिडराइट Zackwehdex

यह थ्रेड-सुरक्षित नहीं है क्योंकि "सिंक्रनाइज़" विधियों के दौरान संग्रह को बदला जा सकता है। यह बिल्कुल धागा सुरक्षा का हिस्सा है। Clear()अन्य कॉल के बाद एक थ्रेड कॉल पर विचार करें this[index]लेकिन लॉक सक्रिय होने से पहले। indexअब उपयोग करने के लिए सुरक्षित नहीं है और जब यह अंततः निष्पादित होता है तो एक अपवाद फेंक देगा।
सनकैट २२

2

आप अधिक आदिम का उपयोग भी कर सकते हैं

Monitor.Enter(lock);
Monitor.Exit(lock);

जो लॉक का उपयोग करता है (इस पोस्ट को देखें C # उस ऑब्जेक्ट को लॉक करना जो लॉक ब्लॉक में पुन: असाइन किया गया है )।

यदि आप कोड में अपवादों की अपेक्षा कर रहे हैं तो यह सुरक्षित नहीं है, लेकिन यह आपको निम्नलिखित की तरह कुछ करने की अनुमति देता है:

using System;
using System.Collections.Generic;
using System.Threading;
using System.Linq;

public class Something
{
    private readonly object _lock;
    private readonly List<string> _contents;

    public Something()
    {
        _lock = new object();

        _contents = new List<string>();
    }

    public Modifier StartModifying()
    {
        return new Modifier(this);
    }

    public class Modifier : IDisposable
    {
        private readonly Something _thing;

        public Modifier(Something thing)
        {
            _thing = thing;

            Monitor.Enter(Lock);
        }

        public void OneOfLotsOfDifferentOperations(string input)
        {
            DoSomethingWith(input);
        }

        private void DoSomethingWith(string input)
        {
            Contents.Add(input);
        }

        private List<string> Contents
        {
            get { return _thing._contents; }
        }

        private object Lock
        {
            get { return _thing._lock; }
        }

        public void Dispose()
        {
            Monitor.Exit(Lock);
        }
    }
}

public class Caller
{
    public void Use(Something thing)
    {
        using (var modifier = thing.StartModifying())
        {
            modifier.OneOfLotsOfDifferentOperations("A");
            modifier.OneOfLotsOfDifferentOperations("B");

            modifier.OneOfLotsOfDifferentOperations("A");
            modifier.OneOfLotsOfDifferentOperations("A");
            modifier.OneOfLotsOfDifferentOperations("A");
        }
    }
}

इसके बारे में एक अच्छी बात यह है कि आपको ऑपरेशन की श्रृंखला की अवधि के लिए लॉक मिलेगा (प्रत्येक ऑपरेशन में लॉक करने के बजाय)। जिसका अर्थ है कि आउटपुट सही क्रम में आना चाहिए (इसका उपयोग बाहरी प्रक्रिया से स्क्रीन पर कुछ आउटपुट प्राप्त कर रहा था)

मुझे वास्तव में थ्रेडसेफ़लिस्ट की सादगी + पारदर्शिता पसंद है + जो दुर्घटनाओं को रोकने में महत्वपूर्ण बिट करता है


2

.NET कोर (किसी भी संस्करण) में, आप ImmutableList का उपयोग कर सकते हैं , जिसमें सभी की कार्यक्षमता है List<T>


1

मुझे विश्वास है कि _list.ToList()आप एक प्रति बना लेंगे। यदि आपको इस तरह की आवश्यकता हो तो आप इसे क्वेरी भी कर सकते हैं:

_list.Select("query here").ToList(); 

वैसे भी, msdn का कहना है कि यह वास्तव में एक प्रति है और केवल एक संदर्भ नहीं है। ओह, और हां, आपको सेट विधि में लॉक करने की आवश्यकता होगी क्योंकि अन्य ने इंगित किया है।


1

ऐसा लगता है कि यह खोजने वाले कई लोग चाहते हैं कि थ्रेड सेफ इंडेक्स डायनामिक रूप से संग्रहित हो। सबसे करीबी और सबसे आसान चीज जो मैं जानता हूं।

System.Collections.Concurrent.ConcurrentDictionary<int, YourDataType>

यदि आप सामान्य अनुक्रमण व्यवहार चाहते हैं, तो आपको यह सुनिश्चित करने की आवश्यकता होगी कि आपकी कुंजी ठीक से क्षीण हो। यदि आप सावधान हैं। आपके द्वारा जोड़े गए किसी भी नए कुंजी मूल्य जोड़े के लिए .count कुंजी के रूप में पर्याप्त हो सकता है।


1
जब कुंजी की गलती नहीं थी तो चाबी को क्यों खराब किया जाना चाहिए?
सनकैट २२


1

मैं किसी को भी List<T>बहु-थ्रेडिंग परिदृश्यों में निपटने के लिए सुझाव दूंगा कि वे विशेष रूप से इम्यूटेबल कलेक्शंस पर नज़र डालें।

आपके पास होने पर मुझे यह बहुत उपयोगी लगा है:

  1. सूची में अपेक्षाकृत कम आइटम
  2. इतने सारे पढ़ने / लिखने के संचालन नहीं
  3. समवर्ती पहुँच का एक बहुत (यानी कई मोड जो पठन मोड में सूची तक पहुँचते हैं)

यह तब भी उपयोगी हो सकता है जब आपको किसी प्रकार के लेन-देन के व्यवहार को लागू करने की आवश्यकता होती है (अर्थात असफल होने की स्थिति में एक इन्सर्ट / अपडेट / डिलीट ऑपरेशन को वापस करें)


-1

यहाँ वह वर्ग है जो आपने माँगा था:

namespace AI.Collections {
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;
    using System.Runtime.Serialization;
    using System.Threading.Tasks;
    using System.Threading.Tasks.Dataflow;

    /// <summary>
    ///     Just a simple thread safe collection.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <value>Version 1.5</value>
    /// <remarks>TODO replace locks with AsyncLocks</remarks>
    [DataContract( IsReference = true )]
    public class ThreadSafeList<T> : IList<T> {
        /// <summary>
        ///     TODO replace the locks with a ReaderWriterLockSlim
        /// </summary>
        [DataMember]
        private readonly List<T> _items = new List<T>();

        public ThreadSafeList( IEnumerable<T> items = null ) { this.Add( items ); }

        public long LongCount {
            get {
                lock ( this._items ) {
                    return this._items.LongCount();
                }
            }
        }

        public IEnumerator<T> GetEnumerator() { return this.Clone().GetEnumerator(); }

        IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(); }

        public void Add( T item ) {
            if ( Equals( default( T ), item ) ) {
                return;
            }
            lock ( this._items ) {
                this._items.Add( item );
            }
        }

        public Boolean TryAdd( T item ) {
            try {
                if ( Equals( default( T ), item ) ) {
                    return false;
                }
                lock ( this._items ) {
                    this._items.Add( item );
                    return true;
                }
            }
            catch ( NullReferenceException ) { }
            catch ( ObjectDisposedException ) { }
            catch ( ArgumentNullException ) { }
            catch ( ArgumentOutOfRangeException ) { }
            catch ( ArgumentException ) { }
            return false;
        }

        public void Clear() {
            lock ( this._items ) {
                this._items.Clear();
            }
        }

        public bool Contains( T item ) {
            lock ( this._items ) {
                return this._items.Contains( item );
            }
        }

        public void CopyTo( T[] array, int arrayIndex ) {
            lock ( this._items ) {
                this._items.CopyTo( array, arrayIndex );
            }
        }

        public bool Remove( T item ) {
            lock ( this._items ) {
                return this._items.Remove( item );
            }
        }

        public int Count {
            get {
                lock ( this._items ) {
                    return this._items.Count;
                }
            }
        }

        public bool IsReadOnly { get { return false; } }

        public int IndexOf( T item ) {
            lock ( this._items ) {
                return this._items.IndexOf( item );
            }
        }

        public void Insert( int index, T item ) {
            lock ( this._items ) {
                this._items.Insert( index, item );
            }
        }

        public void RemoveAt( int index ) {
            lock ( this._items ) {
                this._items.RemoveAt( index );
            }
        }

        public T this[ int index ] {
            get {
                lock ( this._items ) {
                    return this._items[ index ];
                }
            }
            set {
                lock ( this._items ) {
                    this._items[ index ] = value;
                }
            }
        }

        /// <summary>
        ///     Add in an enumerable of items.
        /// </summary>
        /// <param name="collection"></param>
        /// <param name="asParallel"></param>
        public void Add( IEnumerable<T> collection, Boolean asParallel = true ) {
            if ( collection == null ) {
                return;
            }
            lock ( this._items ) {
                this._items.AddRange( asParallel
                                              ? collection.AsParallel().Where( arg => !Equals( default( T ), arg ) )
                                              : collection.Where( arg => !Equals( default( T ), arg ) ) );
            }
        }

        public Task AddAsync( T item ) {
            return Task.Factory.StartNew( () => { this.TryAdd( item ); } );
        }

        /// <summary>
        ///     Add in an enumerable of items.
        /// </summary>
        /// <param name="collection"></param>
        public Task AddAsync( IEnumerable<T> collection ) {
            if ( collection == null ) {
                throw new ArgumentNullException( "collection" );
            }

            var produce = new TransformBlock<T, T>( item => item, new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = Environment.ProcessorCount } );

            var consume = new ActionBlock<T>( action: async obj => await this.AddAsync( obj ), dataflowBlockOptions: new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = Environment.ProcessorCount } );
            produce.LinkTo( consume );

            return Task.Factory.StartNew( async () => {
                collection.AsParallel().ForAll( item => produce.SendAsync( item ) );
                produce.Complete();
                await consume.Completion;
            } );
        }

        /// <summary>
        ///     Returns a new copy of all items in the <see cref="List{T}" />.
        /// </summary>
        /// <returns></returns>
        public List<T> Clone( Boolean asParallel = true ) {
            lock ( this._items ) {
                return asParallel
                               ? new List<T>( this._items.AsParallel() )
                               : new List<T>( this._items );
            }
        }

        /// <summary>
        ///     Perform the <paramref name="action" /> on each item in the list.
        /// </summary>
        /// <param name="action">
        ///     <paramref name="action" /> to perform on each item.
        /// </param>
        /// <param name="performActionOnClones">
        ///     If true, the <paramref name="action" /> will be performed on a <see cref="Clone" /> of the items.
        /// </param>
        /// <param name="asParallel">
        ///     Use the <see cref="ParallelQuery{TSource}" /> method.
        /// </param>
        /// <param name="inParallel">
        ///     Use the
        ///     <see
        ///         cref="Parallel.ForEach{TSource}(System.Collections.Generic.IEnumerable{TSource},System.Action{TSource})" />
        ///     method.
        /// </param>
        public void ForEach( Action<T> action, Boolean performActionOnClones = true, Boolean asParallel = true, Boolean inParallel = false ) {
            if ( action == null ) {
                throw new ArgumentNullException( "action" );
            }
            var wrapper = new Action<T>( obj => {
                try {
                    action( obj );
                }
                catch ( ArgumentNullException ) {
                    //if a null gets into the list then swallow an ArgumentNullException so we can continue adding
                }
            } );
            if ( performActionOnClones ) {
                var clones = this.Clone( asParallel: asParallel );
                if ( asParallel ) {
                    clones.AsParallel().ForAll( wrapper );
                }
                else if ( inParallel ) {
                    Parallel.ForEach( clones, wrapper );
                }
                else {
                    clones.ForEach( wrapper );
                }
            }
            else {
                lock ( this._items ) {
                    if ( asParallel ) {
                        this._items.AsParallel().ForAll( wrapper );
                    }
                    else if ( inParallel ) {
                        Parallel.ForEach( this._items, wrapper );
                    }
                    else {
                        this._items.ForEach( wrapper );
                    }
                }
            }
        }

        /// <summary>
        ///     Perform the <paramref name="action" /> on each item in the list.
        /// </summary>
        /// <param name="action">
        ///     <paramref name="action" /> to perform on each item.
        /// </param>
        /// <param name="performActionOnClones">
        ///     If true, the <paramref name="action" /> will be performed on a <see cref="Clone" /> of the items.
        /// </param>
        /// <param name="asParallel">
        ///     Use the <see cref="ParallelQuery{TSource}" /> method.
        /// </param>
        /// <param name="inParallel">
        ///     Use the
        ///     <see
        ///         cref="Parallel.ForEach{TSource}(System.Collections.Generic.IEnumerable{TSource},System.Action{TSource})" />
        ///     method.
        /// </param>
        public void ForAll( Action<T> action, Boolean performActionOnClones = true, Boolean asParallel = true, Boolean inParallel = false ) {
            if ( action == null ) {
                throw new ArgumentNullException( "action" );
            }
            var wrapper = new Action<T>( obj => {
                try {
                    action( obj );
                }
                catch ( ArgumentNullException ) {
                    //if a null gets into the list then swallow an ArgumentNullException so we can continue adding
                }
            } );
            if ( performActionOnClones ) {
                var clones = this.Clone( asParallel: asParallel );
                if ( asParallel ) {
                    clones.AsParallel().ForAll( wrapper );
                }
                else if ( inParallel ) {
                    Parallel.ForEach( clones, wrapper );
                }
                else {
                    clones.ForEach( wrapper );
                }
            }
            else {
                lock ( this._items ) {
                    if ( asParallel ) {
                        this._items.AsParallel().ForAll( wrapper );
                    }
                    else if ( inParallel ) {
                        Parallel.ForEach( this._items, wrapper );
                    }
                    else {
                        this._items.ForEach( wrapper );
                    }
                }
            }
        }
    }
}

जैसे ही मैं क्लास अपडेट करता हूं, Google ड्राइव पर संस्करण अपडेट हो जाता है। uberscraper.blogspot.com/2012/12/c-thread-safe-list.html
प्रोटीज डिस

क्यों this.GetEnumerator();जब @Tejs का सुझाव है this.Clone().GetEnumerator();?
करूर

क्यों [DataContract( IsReference = true )]?
करूर

नवीनतम संस्करण अब GitHub पर है! github.com/AIBrain/Lib
इन्डोनेशियाई

मैंने ऐड () के तरीकों में दो छोटे बग ढूंढे और तय किए। FYI करें।
प्रोटीजियस

-3

मूल रूप से यदि आप सुरक्षित रूप से गणना करना चाहते हैं, तो आपको लॉक का उपयोग करने की आवश्यकता है।

कृपया इस पर MSDN का संदर्भ लें। http://msdn.microsoft.com/en-us/library/6sh2ey19.aspx

यहाँ MSDN का हिस्सा है जिसे आप रुचि ले सकते हैं:

सार्वजनिक स्थैतिक (विज़ुअल बेसिक में साझा) इस प्रकार के सदस्य थ्रेड सुरक्षित हैं। किसी भी उदाहरण के सदस्यों को धागा सुरक्षित होने की गारंटी नहीं है।

एक सूची कई पाठकों को समवर्ती रूप से समर्थन दे सकती है, जब तक कि संग्रह को संशोधित नहीं किया जाता है। एक संग्रह के माध्यम से भर्ती आंतरिक रूप से एक धागा-सुरक्षित प्रक्रिया नहीं है। दुर्लभ मामले में जहां एक एन्यूमरेशन एक या अधिक लेखन एक्सेस के साथ प्रतिस्पर्धा करता है, थ्रेड सुरक्षा सुनिश्चित करने का एकमात्र तरीका पूरी गणना के दौरान संग्रह को लॉक करना है। संग्रह को पढ़ने और लिखने के लिए कई थ्रेड्स द्वारा एक्सेस करने की अनुमति देने के लिए, आपको अपना स्वयं का सिंक्रनाइज़ेशन लागू करना होगा।


2
सच नहीं है। आप समवर्ती सेट का उपयोग कर सकते हैं।
एनेव्स

-3

यहां लॉक के बिना थ्रेड सुरक्षित सूची के लिए वर्ग है

 public class ConcurrentList   
    {
        private long _i = 1;
        private ConcurrentDictionary<long, T> dict = new ConcurrentDictionary<long, T>();  
        public int Count()
        {
            return dict.Count;
        }
         public List<T> ToList()
         {
            return dict.Values.ToList();
         }

        public T this[int i]
        {
            get
            {
                long ii = dict.Keys.ToArray()[i];
                return dict[ii];
            }
        }
        public void Remove(T item)
        {
            T ov;
            var dicItem = dict.Where(c => c.Value.Equals(item)).FirstOrDefault();
            if (dicItem.Key > 0)
            {
                dict.TryRemove(dicItem.Key, out ov);
            }
            this.CheckReset();
        }
        public void RemoveAt(int i)
        {
            long v = dict.Keys.ToArray()[i];
            T ov;
            dict.TryRemove(v, out ov);
            this.CheckReset();
        }
        public void Add(T item)
        {
            dict.TryAdd(_i, item);
            _i++;
        }
        public IEnumerable<T> Where(Func<T, bool> p)
        {
            return dict.Values.Where(p);
        }
        public T FirstOrDefault(Func<T, bool> p)
        {
            return dict.Values.Where(p).FirstOrDefault();
        }
        public bool Any(Func<T, bool> p)
        {
            return dict.Values.Where(p).Count() > 0 ? true : false;
        }
        public void Clear()
        {
            dict.Clear();
        }
        private void CheckReset()
        {
            if (dict.Count == 0)
            {
                this.Reset();
            }
        }
        private void Reset()
        {
            _i = 1;
        }
    }


_i ++ थ्रेडसेफ़ नहीं है। जब आप इसे बढ़ाते हैं तो आपको परमाणु जोड़ का उपयोग करना पड़ता है और संभवत: यह अस्थिर भी होता है। CheckReset () थ्रेडसेफ़ नहीं है। सशर्त जांच और रीसेट () में कॉल के बीच कुछ भी हो सकता है। अपनी स्वयं की बहु-उपयोगिताएँ न लिखें।
क्रिस रोलिंस

-15

lockऐसा करने के लिए कथन का उपयोग करें। ( अधिक जानकारी के लिए यहां पढ़ें। )

private List<T> _list;

private List<T> MyT
{
    get { return _list; }
    set
    {
        //Lock so only one thread can change the value at any given time.
        lock (_list)
        {
            _list = value;
        }
    }
}

FYI करें शायद यह वही नहीं है जो आपका पूछ रहा है - आप संभवतः अपने कोड में बाहर ताला लगाना चाहते हैं, लेकिन मैं ऐसा नहीं मान सकता। पर एक नजर हैlockकीवर्ड और अपनी विशिष्ट स्थिति में इसके उपयोग को दर्जी करें।

यदि आपको जरूरत है, तो आप चर का उपयोग करके ब्लॉक और ब्लॉक lockदोनों कर सकते हैं, जिससे एक ही समय में रीड / राइट नहीं हो सकता है।getset_list


1
यह उसकी समस्या को हल करने के लिए नहीं जा रहा है; यह केवल थ्रेड को संदर्भ सेट करने से रोकता है, सूची में नहीं जोड़ता है।
तीज 19

और क्या होगा यदि एक धागा मूल्य निर्धारित कर रहा है जबकि दूसरा संग्रह को पुनरावृत्त कर रहा है (यह आपके कोड के साथ संभव है)।
Xaqron 19

जैसा मैंने कहा, लॉक को कोड में आगे ले जाना होगा। यह लॉक स्टेटमेंट का उपयोग करने का एक उदाहरण है।
जोश एम।

2
@Joel Mueller: यकीन है, अगर आप उस तरह कुछ मूर्खतापूर्ण उदाहरण निर्माता। मैं केवल यह बताने की कोशिश कर रहा हूं कि पूछने वाले को lockबयान में देखना चाहिए । इसी तरह के उदाहरण का उपयोग करते हुए मैं तर्क दे सकता हूं कि हम छोरों के लिए उपयोग नहीं करना चाहिए क्योंकि आप आवेदन को मुश्किल से किसी भी प्रयास के साथ रोक सकते हैं:for (int x = 0; x >=0; x += 0) { /* Infinite loop, oops! */ }
जोश एम।

5
मैंने कभी दावा नहीं किया कि आपके कोड का तात्कालिक गतिरोध है। यह निम्नलिखित कारणों से इस विशेष प्रश्न का एक बुरा उत्तर है: 1) यह सूची की सामग्री को सूची की गणना के दौरान, या एक ही बार में दो थ्रेड द्वारा संशोधित किए जाने से बचाता नहीं है। 2) सेटर को लॉक करना लेकिन नहीं गेटर का मतलब संपत्ति वास्तव में थ्रेड-सुरक्षित नहीं है। 3) किसी भी संदर्भ पर ताला लगाना जो वर्ग के बाहर से सुलभ है, व्यापक रूप से एक बुरा अभ्यास माना जाता है, क्योंकि यह नाटकीय रूप से आकस्मिक गतिरोध की संभावना को बढ़ाता है। इसीलिए lock (this)और lock (typeof(this))बड़े नहीं-नहीं हैं।
जोएल मुलर
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.