ReaderWriterLockSlim एक साधारण लॉक से बेहतर कब है?


80

मैं इस कोड के साथ ReaderWriterLock पर एक बहुत ही मूर्खतापूर्ण बेंचमार्क कर रहा हूं, जहां पढ़ना 4 गुना अधिक बार होता है:

class Program
{
    static void Main()
    {
        ISynchro[] test = { new Locked(), new RWLocked() };

        Stopwatch sw = new Stopwatch();

        foreach ( var isynchro in test )
        {
            sw.Reset();
            sw.Start();
            Thread w1 = new Thread( new ParameterizedThreadStart( WriteThread ) );
            w1.Start( isynchro );

            Thread w2 = new Thread( new ParameterizedThreadStart( WriteThread ) );
            w2.Start( isynchro );

            Thread r1 = new Thread( new ParameterizedThreadStart( ReadThread ) );
            r1.Start( isynchro );

            Thread r2 = new Thread( new ParameterizedThreadStart( ReadThread ) );
            r2.Start( isynchro );

            w1.Join();
            w2.Join();
            r1.Join();
            r2.Join();
            sw.Stop();

            Console.WriteLine( isynchro.ToString() + ": " + sw.ElapsedMilliseconds.ToString() + "ms." );
        }

        Console.WriteLine( "End" );
        Console.ReadKey( true );
    }

    static void ReadThread(Object o)
    {
        ISynchro synchro = (ISynchro)o;

        for ( int i = 0; i < 500; i++ )
        {
            Int32? value = synchro.Get( i );
            Thread.Sleep( 50 );
        }
    }

    static void WriteThread( Object o )
    {
        ISynchro synchro = (ISynchro)o;

        for ( int i = 0; i < 125; i++ )
        {
            synchro.Add( i );
            Thread.Sleep( 200 );
        }
    }

}

interface ISynchro
{
    void Add( Int32 value );
    Int32? Get( Int32 index );
}

class Locked:List<Int32>, ISynchro
{
    readonly Object locker = new object();

    #region ISynchro Members

    public new void Add( int value )
    {
        lock ( locker ) 
            base.Add( value );
    }

    public int? Get( int index )
    {
        lock ( locker )
        {
            if ( this.Count <= index )
                return null;
            return this[ index ];
        }
    }

    #endregion
    public override string ToString()
    {
        return "Locked";
    }
}

class RWLocked : List<Int32>, ISynchro
{
    ReaderWriterLockSlim locker = new ReaderWriterLockSlim();

    #region ISynchro Members

    public new void Add( int value )
    {
        try
        {
            locker.EnterWriteLock();
            base.Add( value );
        }
        finally
        {
            locker.ExitWriteLock();
        }
    }

    public int? Get( int index )
    {
        try
        {
            locker.EnterReadLock();
            if ( this.Count <= index )
                return null;
            return this[ index ];
        }
        finally
        {
            locker.ExitReadLock();
        }
    }

    #endregion

    public override string ToString()
    {
        return "RW Locked";
    }
}

लेकिन मुझे लगता है कि दोनों कमोबेश इसी तरह से प्रदर्शन करते हैं:

Locked: 25003ms.
RW Locked: 25002ms.
End

यहां तक ​​कि पढ़ने को 20 गुना अधिक बार लिखते हैं कि प्रदर्शन अभी भी (लगभग) समान है।

क्या मुझसे यहां कुछ गलत हो रहा है?

सधन्यवाद।


3
btw, अगर मैं नींद निकालता हूं: "लॉक किया गया: 89ms। RW लॉक्ड: 32ms।" - या संख्याओं को टकराते हुए: "लॉक किया गया: 1871ms। RW लॉक्ड: 2506ms।"
मार्क Gravell

1
तो मैं सोता है निकालने के लिए, बंद कर दिया तेजी से rwlocked है
vtortola

1
ऐसा इसलिए है क्योंकि आप जिस कोड को सिंक्रनाइज़ कर रहे हैं, वह किसी भी विवाद को बनाने के लिए बहुत तेज़ है। हंस का जवाब, और मेरा संपादन देखें।
दान ताओ

जवाबों:


107

आपके उदाहरण में, नींद का मतलब है कि आम तौर पर कोई विवाद नहीं है। एक अनियंत्रित ताला बहुत तेज है। इस बात के लिए, आपको एक कंटैक्टेड लॉक की आवश्यकता होगी ; अगर उस विवाद में कुछ लिखा गया है, तो उन्हें लगभग उसी ( lockजल्दी भी हो सकता है) होना चाहिए - लेकिन अगर यह ज्यादातर पढ़ा जाता है (लेखन विवाद के साथ शायद ही कभी), मैं उम्मीद करता हूं कि ReaderWriterLockSlimताला आउट-प्रदर्शन करेगा lock

व्यक्तिगत रूप से, मैं संदर्भ-अदला-बदली का उपयोग करते हुए यहां एक और रणनीति पसंद करता हूं - इसलिए पढ़ता है कि कभी भी बिना जांचे / लॉकिंग के पढ़ सकता है / आदि लिखता है, एक क्लोन कॉपी में अपना परिवर्तन करता है , फिर Interlocked.CompareExchangeसंदर्भ को स्वैप करने के लिए उपयोग करता है (यदि कोई अन्य धागा है तो उनके परिवर्तन को फिर से लागू करें) अंतरिम में संदर्भ उत्परिवर्तित)।


1
कॉपी-एंड-स्वैप-ऑन-राइट निश्चित रूप से यहां जाने का तरीका है।
LBushkin

24
गैर-लॉकिंग कॉपी-एंड-स्वैप-ऑन-राइट पूरी तरह से संसाधन विवाद या पाठकों के लिए लॉक करने से बचा जाता है; विवाद होने पर लेखकों के लिए यह तेज़ है, लेकिन विवाद की उपस्थिति में बुरी तरह से लॉग-जाम हो सकता है। यह अच्छा हो सकता है कि लेखक प्रतिलिपि बनाने से पहले एक ताला प्राप्त कर लें (पाठक लॉक की परवाह नहीं करेंगे) और स्वैप करने के बाद ताला जारी करें। अन्यथा यदि 100 थ्रेड्स प्रत्येक एक साथ अपडेट करने का प्रयास करते हैं, तो वे सबसे खराब स्थिति में औसतन लगभग 50 कॉपी-अपडेट-कोशिश-स्वैप ऑपरेशन प्रत्येक (5,000 कुल कॉपी-अपडेट-कोशिश-स्वैप ऑपरेशन 100 अपडेट करने के लिए) कर सकते हैं।
सुपरकाट

1
यहाँ मुझे लगता है कि यह कैसे दिखना चाहिए, कृपया मुझे बताएं कि क्या गलत है:bool bDone=false; while(!bDone) { object origObj = theObj; object newObj = origObj.DeepCopy(); // then make changes to newObj if(Interlocked.CompareExchange(ref theObj, tempObj, newObj)==origObj) bDone=true; }
mcmillab

1
मूल रूप से @mcmillab, हाँ; कुछ साधारण मामलों में (आमतौर पर किसी एकल मान को लपेटते समय, या जहां यह मायने नहीं रखता है अगर बाद में वे ओवर-राइट चीजें लिखते हैं जो उन्होंने कभी नहीं देखीं) तो आप हार भी सकते हैं Interlockedऔर सिर्फ एक volatileक्षेत्र का उपयोग कर सकते हैं और बस इसे असाइन कर सकते हैं - लेकिन अगर आप की जरूरत है सुनिश्चित करें कि आपके परिवर्तन पूरी तरह से खो जाना नहीं था, तो Interlockedआदर्श है
मार्क Gravell

3
@ jnm2: संग्रह तुलना का उपयोग बहुत अधिक करते हैं, लेकिन मुझे नहीं लगता कि वे पूरी नकल करते हैं। CAS + लॉक आवश्यक रूप से निरर्थक नहीं है, यदि कोई इसका उपयोग इस संभावना के लिए करता है कि सभी लेखक लॉक का अधिग्रहण नहीं करेंगे। उदाहरण के लिए, एक संसाधन जिसके लिए विवाद आम तौर पर दुर्लभ होगा, लेकिन कभी-कभी गंभीर हो सकता है, इसमें एक ताला और एक झंडा हो सकता है जो दर्शाता है कि क्या लेखकों को इसे प्राप्त करना चाहिए। यदि झंडा स्पष्ट है, तो लेखक बस एक सीएएस करते हैं और यदि यह सफल होता है, तो वे अपने रास्ते पर चले जाते हैं। एक लेखक जो सीएएस को दो बार से अधिक विफल करता है, हालांकि, झंडा सेट कर सकता है; झंडा तब सेट रह सकता है ...
सुपरकैट

23

मेरे स्वयं के परीक्षणों से संकेत मिलता है कि ReaderWriterLockSlimसामान्य की तुलना में लगभग 5x ओवरहेड है lock। आरडब्ल्यूएलएस के लिए इसका मतलब है कि एक सादा पुराने ताला से आगे निकलने के लिए निम्न स्थितियां आमतौर पर घटित होंगी।

  • पाठकों की संख्या लेखकों को काफी प्रभावित करती है।
  • अतिरिक्त ओवरहेड को दूर करने के लिए लॉक को लंबे समय तक रखना होगा।

अधिकांश वास्तविक अनुप्रयोगों में ये दो स्थितियाँ उस अतिरिक्त ओवरहेड को दूर करने के लिए पर्याप्त नहीं हैं। आपके कोड में विशेष रूप से, ताले इतने कम समय के लिए आयोजित किए जाते हैं कि लॉक ओवरहेड संभवतः वर्चस्व कारक होगा। यदि आप Thread.Sleepलॉक के अंदर उन कॉल को स्थानांतरित करने के लिए थे , तो आप शायद एक अलग परिणाम प्राप्त करेंगे।


17

इस कार्यक्रम में कोई विवाद नहीं है। Get and Add मेथड्स कुछ नैनोसेकंड में निष्पादित होते हैं। कई थ्रेड्स जो सटीक समय पर उन तरीकों से टकराते हैं वे गायब हो जाते हैं।

एक थ्रेड रखो। सो (1) उन्हें कॉल करें और अंतर देखने के लिए थ्रेड्स से नींद को हटा दें।


12

संपादित करें 2 : बस से Thread.Sleepकॉल हटा रहा है ReadThreadऔर WriteThread, मैंने Lockedबेहतर प्रदर्शन किया RWLocked। मेरा मानना ​​है कि हंस ने यहाँ सिर पर कील ठोंकी; आपके तरीके बहुत तेज़ हैं और कोई विवाद नहीं है। जब मैं जोड़ा Thread.Sleep(1)करने के लिए Getऔर Addकरने के तरीकों Lockedऔर RWLocked(और इस्तेमाल किया 4 पढ़ा 1 लिखने धागा के खिलाफ धागे), RWLockedके बंद पैंट को हरा Locked


संपादित करें : ठीक है, अगर मैं वास्तव में सोच रहा था जब मैंने पहली बार इस उत्तर को पोस्ट किया था, तो मुझे कम से कम एहसास हुआ होगा कि आपने Thread.Sleepकॉल को वहां क्यों रखा था: आप लिखने की तुलना में अधिक बार होने वाले रीड्स के परिदृश्य को पुन: उत्पन्न करने की कोशिश कर रहे थे। यह सिर्फ ऐसा करने का सही तरीका नहीं है। इसके बजाय, मैं विवाद का एक बड़ा मौका बनाने के लिए आपके Addऔर Getतरीकों के लिए अतिरिक्त उपरि लागू करूंगा (जैसा कि हंस ने सुझाव दिया है ), थ्रेड लिखने की तुलना में अधिक पढ़ने वाले धागे बनाएं (लिखने की तुलना में अधिक लगातार पढ़ने को सुनिश्चित करने के लिए), और Thread.Sleepकॉल को हटा दें ( ReadThreadऔर WriteThreadवास्तव में विवाद को कम करें , जो आप चाहते हैं उसके विपरीत को प्राप्त करना)।


मुझे पसंद है कि आपने अब तक क्या किया है। लेकिन यहाँ कुछ मुद्दे हैं जिन्हें मैं बल्ले से देखता हूँ:

  1. Thread.Sleepकॉल क्यों ? ये सिर्फ एक निरंतर राशि द्वारा आपके निष्पादन समय को फुला रहे हैं, जो प्रदर्शन के परिणामों को कृत्रिम रूप से परिवर्तित करने जा रहा है।
  2. मैं Threadउस कोड में नई वस्तुओं का निर्माण भी शामिल नहीं करूंगा जो आपके द्वारा मापा जाता है Stopwatch। वह कोई तुच्छ वस्तु नहीं है।

ऊपर दिए गए दो मुद्दों को संबोधित करने के बाद, क्या आप एक महत्वपूर्ण अंतर देखेंगे। लेकिन मेरा मानना ​​है कि चर्चा जारी रहने से पहले उन्हें संबोधित किया जाना चाहिए।


+1: # 2 पर अच्छा बिंदु - यह वास्तव में परिणाम तिरछा कर सकता है (प्रत्येक समय के बीच एक ही अंतर, लेकिन अलग-अलग अनुपात)। यद्यपि यदि आप इसे बहुत देर तक चलाते हैं, तो Threadवस्तुओं को बनाने का समय महत्वहीन होने लगेगा।
नेल्सन रॉदरमेल

10

ReaderWriterLockSlimयदि आप कोड का एक हिस्सा लॉक करते हैं जिसे निष्पादित करने के लिए अधिक समय की आवश्यकता होती है, तो आपको एक साधारण लॉक की तुलना में बेहतर प्रदर्शन मिलेगा । इस मामले में पाठक समानांतर काम कर सकते हैं। हासिल करना एक ReaderWriterLockSlimएक साधारण प्रवेश करने की तुलना में अधिक समय लगता है MonitorReaderWriterLockTinyपाठकों-लेखक लॉक के लिए मेरे कार्यान्वयन की जांच करें जो साधारण लॉक स्टेटमेंट से भी तेज है और पाठकों-लेखक की कार्यक्षमता प्रदान करता है: http://i255.wordpress.com/2013/10/05/fast-readerwriterlock-for-net/


मुझे यह पसंद है। लेख मुझे सोच रहा है कि क्या मॉनिटर प्रत्येक प्लेटफॉर्म पर ReaderWriterLockTiny से तेज है?
jnm2

जबकि ReaderWriterLockTiny, ReaderWriterLockSlim (और मॉनीटर) से भी तेज है, एक खामी है: यदि बहुत से पाठक हैं जो काफी समय लेते हैं, तो उन्होंने कभी लेखकों को मौका नहीं दिया (वे लगभग अनिश्चितकाल तक प्रतीक्षा करेंगे) ।ReaderWriterLockSlim दूसरी ओर, प्रबंधित करें। पाठकों / लेखकों के लिए साझा पुन: स्रोत तक पहुंच संतुलन।
टिगोर

3

इस लेख को देखें: http://blogs.msdn.com/b/pedram/archive/2007/10/07/a-performance-comparison-of-readerwriterlockslim-with-readerwriterlock.aspx

आपकी नींदें शायद इतनी लंबी होती हैं कि वे आपकी लॉकिंग / अनलॉकिंग को सांख्यिकीय रूप से महत्वहीन बना देती हैं।


3

अनियंत्रित ताले अधिग्रहित करने के लिए माइक्रोसेकंड के आदेश पर लेते हैं, इसलिए आपके कॉल द्वारा आपके निष्पादन का समय बौना हो जाएगा Sleep


3

जब तक आपके पास मल्टीकोर हार्डवेयर नहीं है (या कम से कम आपके नियोजित उत्पादन वातावरण के समान) आपको यहां एक यथार्थवादी परीक्षा नहीं मिलेगी।

एक अधिक समझदार परीक्षा लॉक के अंदर एक संक्षिप्त विलंब डालकर आपके बंद किए गए कार्यों के जीवनकाल का विस्तार करना होगा । इस तरह से आपको वास्तव ReaderWriterLockSlimमें मूल द्वारा निहित क्रमिकता बनाम बनाम का उपयोग करके जोड़े गए समानांतरवाद के विपरीत होना चाहिएlock()

वर्तमान में, आपके ऑपरेशन द्वारा लिया गया समय लॉक किए गए स्लीप कॉल से उत्पन्न शोर में खो जाता है। या तो मामले में कुल समय ज्यादातर नींद से संबंधित है।

क्या आप सुनिश्चित हैं कि आपके वास्तविक दुनिया के ऐप में समान संख्या में रीड और राइट होंगे? ReaderWriterLockSlimइस मामले के लिए वास्तव में बेहतर है जहां आपके पास कई पाठक और अपेक्षाकृत अनूठे लेखक हैं। 1 लेखक थ्रेड बनाम 3 रीडर थ्रेड्स को ReaderWriterLockSlimबेहतर लाभ प्रदर्शित करना चाहिए , लेकिन किसी भी मामले में आपका परीक्षण आपके अपेक्षित वास्तविक-विश्व पहुंच पैटर्न से मेल खाना चाहिए।


2

मुझे लगता है कि यह आपके द्वारा पाठक और लेखक के धागों की नींद के कारण है।
आपके पढ़े गए धागे में 500 सेंटीमीटर 50 सेंटीमीटर की नींद है जो कि 25000 है


0

ReaderWriterLockSlim एक साधारण लॉक से बेहतर कब है?

जब आपके पास लिखने की तुलना में काफी अधिक है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.