.NET फ्रेमवर्क में समवर्ती HashSet <T>?


151

मेरे पास निम्न वर्ग है।

class Test{
    public HashSet<string> Data = new HashSet<string>();
}

मुझे विभिन्न थ्रेड्स से फ़ील्ड "डेटा" को बदलने की आवश्यकता है, इसलिए मैं अपने वर्तमान थ्रेड-सुरक्षित कार्यान्वयन पर कुछ राय चाहूंगा।

class Test{
    public HashSet<string> Data = new HashSet<string>();

    public void Add(string Val){
            lock(Data) Data.Add(Val);
    }

    public void Remove(string Val){
            lock(Data) Data.Remove(Val);
    }
}

क्या एक बेहतर समाधान है, सीधे क्षेत्र में जाने और इसे कई धागे द्वारा समवर्ती पहुंच से बचाने के लिए?


कैसे के तहत एक संग्रह का उपयोग करने के बारे मेंSystem.Collections.Concurrent
I4V

8
बेशक, इसे निजी बनाएं।
हंस पास्ट

3
एक संगामिति के नजरिए से, मैंने डेटा क्षेत्र के सार्वजनिक होने के अलावा जो कुछ किया है, मैं उससे ज्यादा गलत नहीं हूँ! यदि आप एक चिंता का विषय है तो ReaderWriterLockSlim का उपयोग करके बेहतर रीड परफॉर्मेंस पा सकते हैं। msdn.microsoft.com/en-us/library/…
एलन एल्डर

ReaderWriterLockकई पाठकों और एक लेखक के @AllanElder सहायक (कुशल) होगा। हमें पता है कि क्या यह ओपी के लिए मामला है
श्रीराम सक्थिवेल

2
वर्तमान कार्यान्वयन वास्तव में 'समवर्ती' नहीं है :) यह सिर्फ धागा-सुरक्षित है।
अपरिभाषित

जवाबों:


164

आपका कार्यान्वयन सही है। .NET फ्रेमवर्क दुर्भाग्य से, अंतर्निहित समवर्ती प्रकार प्रदान नहीं करता है। हालाँकि, कुछ वर्कअराउंड हैं।

समवर्ती (अनुशंसित)

यह पहला ConcurrentDictionary<TKey, TValue>नाम नेमस्पेस में क्लास का उपयोग करने के लिए है System.Collections.Concurrent। मामले में, मान व्यर्थ है, इसलिए हम एक साधारण byte(1 बाइट मेमोरी में) का उपयोग कर सकते हैं ।

private ConcurrentDictionary<string, byte> _data;

यह अनुशंसित विकल्प है क्योंकि प्रकार थ्रेड-सुरक्षित है और आपको एक HashSet<T>कुंजी के अलावा एक ही लाभ प्रदान करता है और मूल्य अलग-अलग ऑब्जेक्ट हैं।

स्रोत: सामाजिक MSDN

ConcurrentBag

यदि आपको डुप्लिकेट प्रविष्टियों के बारे में कोई आपत्ति नहीं है, तो आप ConcurrentBag<T>पिछली कक्षा के समान नामस्थान में कक्षा का उपयोग कर सकते हैं ।

private ConcurrentBag<string> _data;

स्व कार्यान्वयन

अंत में, जैसा कि आपने किया था, आप अपने स्वयं के डेटा प्रकार को लागू कर सकते हैं, लॉक या अन्य तरीकों से जो .NET आपको थ्रेड-सुरक्षित होने के लिए प्रदान करता है। यहाँ एक महान उदाहरण है: .Net में समवर्ती हाशसेट को कैसे लागू किया जाए

इस समाधान का एकमात्र दोष यह है कि HashSet<T>पढ़ने के संचालन के लिए भी प्रकार आधिकारिक तौर पर समवर्ती पहुंच नहीं है।

मैं लिंक किए गए पोस्ट का कोड उद्धृत करता हूं (मूल रूप से बेन मोशेर द्वारा लिखित )।

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

namespace BlahBlah.Utilities
{
    public class ConcurrentHashSet<T> : IDisposable
    {
        private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
        private readonly HashSet<T> _hashSet = new HashSet<T>();

        #region Implementation of ICollection<T> ...ish
        public bool Add(T item)
        {
            _lock.EnterWriteLock();
            try
            {
                return _hashSet.Add(item);
            }
            finally
            {
                if (_lock.IsWriteLockHeld) _lock.ExitWriteLock();
            }
        }

        public void Clear()
        {
            _lock.EnterWriteLock();
            try
            {
                _hashSet.Clear();
            }
            finally
            {
                if (_lock.IsWriteLockHeld) _lock.ExitWriteLock();
            }
        }

        public bool Contains(T item)
        {
            _lock.EnterReadLock();
            try
            {
                return _hashSet.Contains(item);
            }
            finally
            {
                if (_lock.IsReadLockHeld) _lock.ExitReadLock();
            }
        }

        public bool Remove(T item)
        {
            _lock.EnterWriteLock();
            try
            {
                return _hashSet.Remove(item);
            }
            finally
            {
                if (_lock.IsWriteLockHeld) _lock.ExitWriteLock();
            }
        }

        public int Count
        {
            get
            {
                _lock.EnterReadLock();
                try
                {
                    return _hashSet.Count;
                }
                finally
                {
                    if (_lock.IsReadLockHeld) _lock.ExitReadLock();
                }
            }
        }
        #endregion

        #region Dispose
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
                if (_lock != null)
                    _lock.Dispose();
        }
        ~ConcurrentHashSet()
        {
            Dispose(false);
        }
        #endregion
    }
}

संपादित करें: प्रवेश लॉक विधियों को tryब्लॉक से अलग करें, क्योंकि वे एक अपवाद फेंक सकते हैं और finallyब्लॉक में निहित निर्देशों को निष्पादित कर सकते हैं।


8
कबाड़ मूल्यों के साथ एक शब्दकोष एक सूची है
राल्फ

44
@Ralf खैर, यह एक सेट है, सूची नहीं है, क्योंकि यह अनियंत्रित है।
सेवी

11
"कलेक्शन एंड सिंक्रोनाइजेशन (थ्रेड सेफ्टी)" पर MSDN के लघु दस्तावेज़ के अनुसार , System.Collections और संबंधित नामस्थानों की कक्षाओं को कई थ्रेड्स द्वारा सुरक्षित रूप से पढ़ा जा सकता है। इसका मतलब है कि हैशसेट को कई थ्रेड्स द्वारा सुरक्षित रूप से पढ़ा जा सकता है।
हांक शुल्त्

7
@ ओलिवर, एक संदर्भ प्रति प्रविष्टि में बहुत अधिक मेमोरी का उपयोग करता है, भले ही यह एक nullसंदर्भ हो (संदर्भ 32-बिट रनटाइम में 4 बाइट्स और 64-बिट रनटाइम में 8 बाइट्स की आवश्यकता होती है)। इसलिए, byteरिक्त संरचना, या समान का उपयोग करके मेमोरी फ़ुटप्रिंट को कम किया जा सकता है (या ऐसा नहीं हो सकता है कि रनटाइम तेजी से पहुंच के लिए देशी मेमोरी सीमाओं पर डेटा संरेखित करता है)।
लुकेरो

4
सेल्फ-इम्प्लीमेंटेशन समवर्ती हाशसेट नहीं है, बल्कि थ्रेडशैफसेट है। उन 2 के बीच एक बड़ा अंतर है और यही कारण है कि माइक्रोसेफ्ट ने सिंक्रोनाइज्ड कॉलेक्शंस (लोगों को यह गलत मिला) को छोड़ दिया। "समवर्ती" होने के लिए GetOrAdd आदि जैसे कार्यों को लागू किया जाना चाहिए (जैसे कि शब्दकोश) या किसी अन्य संगोष्ठी को अतिरिक्त लॉकिंग के बिना सुनिश्चित नहीं किया जा सकता है। लेकिन अगर आपको कक्षा के बाहर अतिरिक्त लॉकिंग की आवश्यकता है तो आप शुरू से ही सही हैशसेट का उपयोग क्यों नहीं करते हैं?
जॉर्ज मावित्सकिस

36

के बजाय एक लपेटकर ConcurrentDictionaryया एक पर ताला लगा HashSetमैं एक वास्तविक ConcurrentHashSetपर आधारित बनाया ConcurrentDictionary

यह क्रियान्वयन आइटम के बिना मूल कार्यों के मूल संचालन का समर्थन करता है HashSetक्योंकि वे समवर्ती परिदृश्यों में कम समझदारी पैदा करते हैं IMO:

var concurrentHashSet = new ConcurrentHashSet<string>(
    new[]
    {
        "hamster",
        "HAMster",
        "bar",
    },
    StringComparer.OrdinalIgnoreCase);

concurrentHashSet.TryRemove("foo");

if (concurrentHashSet.Contains("BAR"))
{
    Console.WriteLine(concurrentHashSet.Count);
}

आउटपुट: २

आप इसे यहाँ NuGet से प्राप्त कर सकते हैं और यहाँ GitHub पर स्रोत देख सकते हैं


3
यह स्वीकृत उत्तर होना चाहिए, महान कार्यान्वयन
स्मरकिंगमैन

नहीं करना चाहिए जोड़ें करने के लिए नाम दिया जा TryAdd इतना है कि यह के अनुरूप हो ConcurrentDictionary ?
नियो

8
@ नो नो ... क्योंकि यह जानबूझकर हैशसेट <टी> शब्दार्थ का उपयोग कर रहा है , जहां आप ऐड कहते हैं और यह एक बूलियन को इंगित करता है कि क्या आइटम जोड़ा गया था (सच), या यह पहले से ही मौजूद है (गलत)। msdn.microsoft.com/en-us/library/bb353005(v=vs.110).aspx
G-Mac

यह ISet<T>इंटरफ़ेस बो वास्तव में HashSet<T>शब्दार्थ से मेल नहीं करना चाहिए ?
नेकरोमैंसर

1
@ नेक्रोमैंसर जैसा कि मैंने उत्तर में कहा था, मुझे नहीं लगता कि समवर्ती कार्यान्वयन में इन सेट विधियों को प्रदान करना समझ में आता है। Overlapsउदाहरण के लिए या तो इसके रन के दौरान इंस्टेंस को लॉक करना होगा, या ऐसा उत्तर देना होगा जो पहले से गलत हो। दोनों विकल्प खराब IMO हैं (और उपभोक्ताओं द्वारा बाहरी रूप से जोड़े जा सकते हैं)।
i3arnon

21

चूंकि किसी और ने इसका उल्लेख नहीं किया है, मैं एक वैकल्पिक दृष्टिकोण प्रदान करूंगा जो आपके विशेष उद्देश्य के लिए उपयुक्त हो सकता है या नहीं भी हो सकता है:

Microsoft अपरिवर्तनीय संग्रह

पीछे MS टीम द्वारा एक ब्लॉग पोस्ट से:

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

ताला लगाने का एक विकल्प अपरिवर्तनीय स्थिति का उपयोग कर रहा है। अपरिवर्तनीय डेटा संरचनाओं को कभी नहीं बदलने की गारंटी दी जाती है और इस प्रकार किसी अन्य पैर की उंगलियों पर कदम रखने की चिंता किए बिना विभिन्न थ्रेड्स के बीच स्वतंत्र रूप से पारित किया जा सकता है।

यह डिज़ाइन हालांकि एक नई समस्या पैदा करता है: आप हर बार पूरे राज्य की नकल किए बिना राज्य में बदलाव कैसे प्रबंधित करते हैं? यह विशेष रूप से मुश्किल है जब संग्रह शामिल होते हैं।

यह वह जगह है जहाँ अपरिवर्तनीय संग्रह आते हैं।

इन संग्रहों में ImmutableHashSet <T> और ImmutableList <T> शामिल हैं

प्रदर्शन

चूंकि अपरिवर्तनीय संग्रह संरचनात्मक साझाकरण को सक्षम करने के लिए नीचे दिए गए ट्री डेटा संरचनाओं का उपयोग करते हैं, इसलिए उनके प्रदर्शन विशेषताओं को परस्पर संग्रह से अलग किया जाता है। जब एक लॉकिंग म्यूटेबल संग्रह की तुलना की जाती है, तो परिणाम लॉक विवाद और एक्सेस पैटर्न पर निर्भर करेगा। हालाँकि, अपरिवर्तनीय संग्रह के बारे में एक अन्य ब्लॉग पोस्ट से लिया गया है :

प्रश्न: मैंने सुना है कि अपरिवर्तनीय संग्रह धीमा है। क्या ये कोई अलग हैं? क्या प्रदर्शन या मेमोरी महत्वपूर्ण होने पर मैं उनका उपयोग कर सकता हूं?

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

दूसरे शब्दों में, कई मामलों में अंतर ध्यान देने योग्य नहीं होगा और आपको सरल विकल्प के साथ जाना चाहिए - जो समवर्ती सेटों के लिए उपयोग करना होगा ImmutableHashSet<T>, क्योंकि आपके पास मौजूदा लॉकिंग उत्परिवर्ती कार्यान्वयन नहीं है! :-)


1
ImmutableHashSet<T>अगर आपकी मंशा साझा थ्रेड को कई थ्रेड्स से अपडेट करना है या क्या मैं यहां कुछ याद कर रहा हूं तो बहुत मदद नहीं करता है?
tugberk

7
@tugberk हाँ और नहीं। चूंकि सेट अपरिवर्तनीय है, इसलिए आपको इसके संदर्भ को अपडेट करना होगा, जो संग्रह स्वयं आपकी मदद नहीं करता है। अच्छी खबर यह है कि आपने साझा संदर्भ को अपडेट करने की समस्या को कई थ्रेड्स से साझा डेटा संरचना को अद्यतन करने की जटिल समस्या को कम कर दिया है। पुस्तकालय आपको ImmutableInterlocked.Update विधि प्रदान करता है ताकि आप उसके साथ मदद कर सकें।
सोरेन बोइसन

1
@ SørenBoisenjust ने संग्रहणीय संग्रह के बारे में पढ़ा और यह पता लगाने की कोशिश की कि उन्हें थ्रेड का सुरक्षित उपयोग कैसे करना है। ImmutableInterlocked.Updateलगता है कि लापता लिंक है। धन्यवाद!
xneg

4

ISet<T>समवर्ती बनाने के बारे में मुश्किल हिस्सा यह है कि सेट विधियां (संघ, अंतर, अंतर) प्रकृति में पुनरावृत्त हैं। बहुत कम से कम आपको दोनों सेटों को लॉक करते हुए ऑपरेशन में शामिल किसी एक सेट के सभी एन सदस्यों पर पुनरावृत्त करना होगा।

ConcurrentDictionary<T,byte>जब आपको पुनरावृत्ति के दौरान पूरे सेट को लॉक करना पड़ता है तो आप इसके फायदे खो देते हैं । लॉक किए बिना, ये ऑपरेशन थ्रेड सुरक्षित नहीं हैं।

के अतिरिक्त उपरि को देखते हुए ConcurrentDictionary<T,byte>, यह शायद हल्का वजन का उपयोग करने के लिए समझदार है HashSet<T>और सिर्फ ताले में सब कुछ घेरना है।

यदि आपको सेट ऑपरेशंस की आवश्यकता नहीं है, तो कुंजी जोड़ते समय मूल्य के रूप में ConcurrentDictionary<T,byte>उपयोग करें और उपयोग default(byte)करें।


2

मैं पूर्ण समाधानों को पसंद करता हूं इसलिए मैंने ऐसा किया: माइंड यू माई काउंट को एक अलग अंदाज़ में लागू किया गया है क्योंकि मैं यह नहीं देखता कि किसी को इसके मूल्यों को गिनने का प्रयास करते समय हैशसेट को पढ़ने से क्यों मना किया जाए।

@Zen, इसे शुरू करने के लिए धन्यवाद।

[DebuggerDisplay("Count = {Count}")]
[Serializable]
public class ConcurrentHashSet<T> : ICollection<T>, ISet<T>, ISerializable, IDeserializationCallback
{
    private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);

    private readonly HashSet<T> _hashSet = new HashSet<T>();

    public ConcurrentHashSet()
    {
    }

    public ConcurrentHashSet(IEqualityComparer<T> comparer)
    {
        _hashSet = new HashSet<T>(comparer);
    }

    public ConcurrentHashSet(IEnumerable<T> collection)
    {
        _hashSet = new HashSet<T>(collection);
    }

    public ConcurrentHashSet(IEnumerable<T> collection, IEqualityComparer<T> comparer)
    {
        _hashSet = new HashSet<T>(collection, comparer);
    }

    protected ConcurrentHashSet(SerializationInfo info, StreamingContext context)
    {
        _hashSet = new HashSet<T>();

        // not sure about this one really...
        var iSerializable = _hashSet as ISerializable;
        iSerializable.GetObjectData(info, context);
    }

    #region Dispose

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
            if (_lock != null)
                _lock.Dispose();
    }

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

    ~ConcurrentHashSet()
    {
        Dispose(false);
    }

    public void OnDeserialization(object sender)
    {
        _hashSet.OnDeserialization(sender);
    }

    public void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        _hashSet.GetObjectData(info, context);
    }

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

    #endregion

    public void Add(T item)
    {
        _lock.EnterWriteLock();
        try
        {
            _hashSet.Add(item);
        }
        finally
        {
            if(_lock.IsWriteLockHeld) _lock.ExitWriteLock();
        }
    }

    public void UnionWith(IEnumerable<T> other)
    {
        _lock.EnterWriteLock();
        _lock.EnterReadLock();
        try
        {
            _hashSet.UnionWith(other);
        }
        finally
        {
            if (_lock.IsWriteLockHeld) _lock.ExitWriteLock();
            if (_lock.IsReadLockHeld) _lock.ExitReadLock();
        }
    }

    public void IntersectWith(IEnumerable<T> other)
    {
        _lock.EnterWriteLock();
        _lock.EnterReadLock();
        try
        {
            _hashSet.IntersectWith(other);
        }
        finally
        {
            if (_lock.IsWriteLockHeld) _lock.ExitWriteLock();
            if (_lock.IsReadLockHeld) _lock.ExitReadLock();
        }
    }

    public void ExceptWith(IEnumerable<T> other)
    {
        _lock.EnterWriteLock();
        _lock.EnterReadLock();
        try
        {
            _hashSet.ExceptWith(other);
        }
        finally
        {
            if (_lock.IsWriteLockHeld) _lock.ExitWriteLock();
            if (_lock.IsReadLockHeld) _lock.ExitReadLock();
        }
    }

    public void SymmetricExceptWith(IEnumerable<T> other)
    {
        _lock.EnterWriteLock();
        try
        {
            _hashSet.SymmetricExceptWith(other);
        }
        finally
        {
            if (_lock.IsWriteLockHeld) _lock.ExitWriteLock();
        }
    }

    public bool IsSubsetOf(IEnumerable<T> other)
    {
        _lock.EnterWriteLock();
        try
        {
            return _hashSet.IsSubsetOf(other);
        }
        finally
        {
            if (_lock.IsWriteLockHeld) _lock.ExitWriteLock();
        }
    }

    public bool IsSupersetOf(IEnumerable<T> other)
    {
        _lock.EnterWriteLock();
        try
        {
            return _hashSet.IsSupersetOf(other);
        }
        finally
        {
            if (_lock.IsWriteLockHeld) _lock.ExitWriteLock();
        }
    }

    public bool IsProperSupersetOf(IEnumerable<T> other)
    {
        _lock.EnterWriteLock();
        try
        {
            return _hashSet.IsProperSupersetOf(other);
        }
        finally
        {
            if (_lock.IsWriteLockHeld) _lock.ExitWriteLock();
        }
    }

    public bool IsProperSubsetOf(IEnumerable<T> other)
    {
        _lock.EnterWriteLock();
        try
        {
            return _hashSet.IsProperSubsetOf(other);
        }
        finally
        {
            if (_lock.IsWriteLockHeld) _lock.ExitWriteLock();
        }
    }

    public bool Overlaps(IEnumerable<T> other)
    {
        _lock.EnterWriteLock();
        try
        {
            return _hashSet.Overlaps(other);
        }
        finally
        {
            if (_lock.IsWriteLockHeld) _lock.ExitWriteLock();
        }
    }

    public bool SetEquals(IEnumerable<T> other)
    {
        _lock.EnterWriteLock();
        try
        {
            return _hashSet.SetEquals(other);
        }
        finally
        {
            if (_lock.IsWriteLockHeld) _lock.ExitWriteLock();
        }
    }

    bool ISet<T>.Add(T item)
    {
        _lock.EnterWriteLock();
        try
        {
            return _hashSet.Add(item);
        }
        finally
        {
            if (_lock.IsWriteLockHeld) _lock.ExitWriteLock();
        }
    }

    public void Clear()
    {
        _lock.EnterWriteLock();
        try
        {
            _hashSet.Clear();
        }
        finally
        {
            if (_lock.IsWriteLockHeld) _lock.ExitWriteLock();
        }
    }

    public bool Contains(T item)
    {
        _lock.EnterWriteLock();
        try
        {
            return _hashSet.Contains(item);
        }
        finally
        {
            if (_lock.IsWriteLockHeld) _lock.ExitWriteLock();
        }
    }

    public void CopyTo(T[] array, int arrayIndex)
    {
        _lock.EnterWriteLock();
        try
        {
            _hashSet.CopyTo(array, arrayIndex);
        }
        finally
        {
            if (_lock.IsWriteLockHeld) _lock.ExitWriteLock();
        }
    }

    public bool Remove(T item)
    {
        _lock.EnterWriteLock();
        try
        {
            return _hashSet.Remove(item);
        }
        finally
        {
            if (_lock.IsWriteLockHeld) _lock.ExitWriteLock();
        }
    }

    public int Count
    {
        get
        {
            _lock.EnterWriteLock();
            try
            {
                return _hashSet.Count;
            }
            finally
            {
                if(_lock.IsWriteLockHeld) _lock.ExitWriteLock();
            }

        }
    }

    public bool IsReadOnly
    {
        get { return false; }
    }
}

ताला ख़त्म हो जाता है ... लेकिन भीतर की हैशसेट का क्या, इसकी मेमोरी कब रिलीज़ होती है?
डेविड रिटेनबैचर

1
@Warappa यह कचरा संग्रहण पर जारी किया गया है। केवल समय मैं चीजों को मैन्युअल रूप से शून्य करता हूं और एक वर्ग के भीतर उनकी पूरी उपस्थिति को साफ करता है, जब विषयों में घटनाएं होती हैं और इस प्रकार एमईई लीक मेमोरी (जैसे कि आप ऑब्जर्वेबल कॉलेक्शन और इसके बदले हुए इवेंट का उपयोग कब करेंगे)। यदि आप विषय के बारे में मेरी समझ में ज्ञान जोड़ सकते हैं तो मैं सुझाव के लिए खुला हूं। मैंने कुछ दिनों तक कचरा संग्रहण पर शोध किया है और हमेशा नई जानकारी के लिए उत्सुक हूं
Dbl

@ AndreasMüller अच्छा जवाब, हालाँकि मैं सोच रहा हूँ कि आप '_lock.EnterWriteock ()?' का उपयोग क्यों कर रहे हैं? '_Lock.EnterReadLock ();' कुछ तरीकों जैसे 'इन्टरसेक्टविथ' से मुझे लगता है कि यहां रीड लुक की कोई आवश्यकता नहीं है क्योंकि राइट लॉक डिफ़ॉल्ट रूप से दर्ज करने पर किसी भी रीडिंग को रोक देगा।
जलाल ने कहा

अगर आपको हमेशा रहना चाहिए EnterWriteLock, तो EnterReadLockभी अस्तित्व क्यों है ? क्या यह रीड लॉक का इस्तेमाल तरीकों के लिए नहीं किया जा सकता है Contains?
ErikE

2
यह एक समवर्ती HashSet नहीं है, बल्कि एक ThreadSafeHashSet है। स्वयं कार्यान्वयन के संबंध में @ZenLulz उत्तर पर मेरी टिप्पणी देखें। मुझे 99% यकीन है कि जो कोई भी उन कार्यान्वयन का उपयोग करता है, उनके आवेदन में एक गंभीर बग होगा।
जॉर्ज मावित्सकिस
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.