2015-09-02 9 views
5

क्या यह सुनिश्चित करें कि केवल 'आखिरी में' धागा जबकि मध्यस्थ धागे ताला प्राप्त नहीं करते एक म्युटेक्स/बंद कर दिया क्षेत्र के लिए उपयोग में दिया जाता है उचित तरीका है ?धागा तुल्यकालन (ताला) है कि केवल करने के लिए विज्ञप्ति अंतिम में धागा

उदाहरण अनुक्रम:

A acquires lock 
B waits 
C waits 
B fails to acquire lock* 
A releases lock 
C acquires lock 

* बी SemaphoreSlim.Wait(CancellationToken) या एक बूलियन Monitor.TryEnter() प्रकार निर्माण में के रूप में ताला या तो एक अपवाद (के माध्यम से प्राप्त करने में विफल चाहिए

मैं कई समान योजनाओं को प्राप्त करने के बारे में सोच सकते हैं। यह (जैसे CancellationTokenSource और SemaphoreSlim का उपयोग करना), लेकिन उनमें से कोई भी विशेष रूप से सुरुचिपूर्ण प्रतीत नहीं होता है।

क्या इस परिदृश्य के लिए कोई आम प्रथा है?

+1

यह एक दिलचस्प सवाल है - तो क्या होगा अगर - 'A' के बीच लॉक जारी करना और 'सी' इसे प्राप्त करना - 'डी' में आता है? मैं सिर्फ उत्सुक हूं - एक परिदृश्य को सोचने की कोशिश कर रहा हूं जहां इस दृष्टिकोण की आवश्यकता होगी। – xxbbcc

+1

मुझे खेद है, समझ में नहीं आया "बी विफल रहता है" इसका मतलब क्या है? थ्रेड खिड़की से थ्रेड जमे हुए? – Kapoor

+0

वांछित परिणाम @xxbbcc कि पिछले-इन (अपने परिदृश्य में डी) सफलतापूर्वक ताला प्राप्त कर लेता है, जबकि मध्यवर्ती धागे नहीं (बी और सी बंद कर दिया खंड में प्रवेश नहीं करते) करते हैं। –

उत्तर

2

इस तरह आप चाहते हैं काम करना चाहिए, यह 1 के एक आकार के साथ एक SemaphoreSlim का उपयोग करता है इसे नियंत्रित करने के। मैं भी एक CancelationToken में गुजर जल्दी ताला के लिए इंतजार को रद्द करने के लिए समर्थन जोड़ा है, यह भी अवरुद्ध करने के बजाय एक कार्य लौटने WaitAsync का समर्थन करता है।

public sealed class LastInLocker : IDisposable 
{ 
    private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1); 
    private CancellationTokenSource _cts = new CancellationTokenSource(); 
    private bool _disposed = false; 

    public void Wait() 
    { 
     Wait(CancellationToken.None); 
    } 

    public void Wait(CancellationToken earlyCancellationToken) 
    { 
     if(_disposed) 
      throw new ObjectDisposedException("LastInLocker"); 

     var token = ReplaceTokenSource(earlyCancellationToken); 
     _semaphore.Wait(token); 
    } 

    public Task WaitAsync() 
    { 
     return WaitAsync(CancellationToken.None); 
    } 

    public async Task WaitAsync(CancellationToken earlyCancellationToken) 
    { 
     if (_disposed) 
      throw new ObjectDisposedException("LastInLocker"); 

     var token = ReplaceTokenSource(earlyCancellationToken); 

     //I await here because if ReplaceTokenSource thows a exception I want the 
     //observing of that exception to be deferred until the caller awaits my 
     //returned task. 
     await _semaphore.WaitAsync(token).ConfigureAwait(false); 
    } 

    public void Release() 
    { 
     if (_disposed) 
      throw new ObjectDisposedException("LastInLocker"); 

     _semaphore.Release(); 
    } 

    private CancellationToken ReplaceTokenSource(CancellationToken earlyCancellationToken) 
    { 
     var newSource = CancellationTokenSource.CreateLinkedTokenSource(earlyCancellationToken); 
     var oldSource = Interlocked.Exchange(ref _cts, newSource); 
     oldSource.Cancel(); 
     oldSource.Dispose(); 

     return newSource.Token; 
    } 

    public void Dispose() 
    { 
     _disposed = true; 

     _semaphore.Dispose(); 
     _cts.Dispose(); 
    } 
} 

यहाँ एक छोटे से परीक्षण कार्यक्रम है कि फिर से पैदा करता है अपने परीक्षण उदाहरण

internal class Program 
{ 
    static LastInLocker locker = new LastInLocker(); 
    private static void Main(string[] args) 
    { 
     Task.Run(() => Test("A")); 
     Thread.Sleep(500); 
     Task.Run(() => Test("B")); 
     Thread.Sleep(500); 
     Task.Run(() => Test("C")); 
     Console.ReadLine(); 
    } 

    private static void Test(string name) 
    { 
     Console.WriteLine("{0} waits for lock", name); 
     try 
     { 
      locker.Wait(); 
      Console.WriteLine("{0} acquires lock", name); 

      Thread.Sleep(4000); 
      locker.Release(); 

      Console.WriteLine("{0} releases lock", name); 
     } 
     catch (Exception) 
     { 
      Console.WriteLine("{0} fails to acquire lock", name); 
     } 
    } 
} 

आउटपुट

 
A waits for lock 
A acquires lock 
B waits for lock 
C waits for lock 
B fails to acquire lock 
A releases lock 
C acquires lock 
C releases lock 
+0

हाय स्कॉट, विस्तृत जवाब के लिए धन्यवाद। यह बहुत ही विधि है कि मैं उपयोग कर रहा है (सिर्फ एक Interlocked.Exchange के साथ आंतरिक ताला की जगह) के करीब है। लेकिन रैपिंग यह एक पुन: प्रयोज्य वर्ग में एक बहुत अच्छा स्पर्श है। –

+1

@AndrewHanlon मैं 'कॉंकेल() 'और नया टोकन असाइनमेंट परमाणु ऑपरेशन होना चाहता था, अन्यथा मैं संभवतः ऐसा ही कर सकता था। मैं थोड़ी देर के साथ टिंग मुझे लगता है कि मैं इसे इंटरलाक्ड एक्सचेंज के साथ कर सकता हूं लेकिन यह लॉक बहुत छोटा होने वाला है और अक्सर अनचाहे होता है इसलिए ओवरहेड खराब नहीं होता है। –

+0

मैं उम्मीद कर रहा था/यह सोच कर कि वहाँ इस परिदृश्य पूरा करने के लिए एक सरल आदिम होना चाहिए, लेकिन यह वास्तव में सही दृष्टिकोण हो सकता है। –

0

इस प्रयास करें:

public interface ILocker 
{ 
    bool GetLock(); 

    void Release(); 
} 

class Locker : ILocker 
{ 
    private long m_NumberOfTimeGetLockWasCalled = 0; 

    private readonly object m_LockingObject = new object(); 

    private readonly object m_LockingObject2 = new object(); 

    public bool GetLock() 
    { 

     long lock_count = 0; 

     var lock_was_taken = false; 

     lock(m_LockingObject) 
     { 
      lock_count = m_NumberOfTimeGetLockWasCalled++; 

      lock_was_taken = Monitor.TryEnter(m_LockingObject2); 

      if (lock_was_taken) 
       return true; 

     } 

     while(!lock_was_taken) 
     { 

      Thread.Sleep(5); 

      lock(m_LockingObject) 
      { 

       if (lock_count != m_NumberOfTimeGetLockWasCalled) 
        return false; 

       lock_was_taken = Monitor.TryEnter(m_LockingObject2); 

       if (lock_was_taken) 
        break; 

      } 


     } 


     return true; 
    } 

    public void Release() 
    { 
     Monitor.Exit(m_LockingObject2); 
    } 
} 
संबंधित मुद्दे