2009-12-27 15 views
17

शायद रात में बहुत देर हो चुकी है, लेकिन मैं ऐसा करने का एक अच्छा तरीका नहीं सोच सकता।.NET रिवर्स सेमफोर?

मैंने एसिंक्रोनस डाउनलोड का एक गुच्छा शुरू कर दिया है, और जब तक प्रोग्राम समाप्त होने से पहले पूरा नहीं हो जाता तब तक मैं इंतजार करना चाहता हूं। इससे मुझे विश्वास होता है कि डाउनलोड शुरू होने पर मुझे कुछ बढ़ाना चाहिए, और इसे खत्म होने पर इसे कम करें। लेकिन फिर मैं गिनती 0 फिर से प्रतीक्षा कैसे करूं?

सेफफोर्स विपरीत तरीके से काम करते हैं जब आप कोई संसाधन उपलब्ध नहीं होते हैं, जब वे सभी उपलब्ध नहीं होते हैं (जब शून्य 0 के बजाय गिनती 0 होती है तो ब्लॉक)।

+0

इस गंध पर लॉकिंग मजेदार –

+0

@ ओरी: वह क्यों है? एमएसडीएन का कहना है कि यह करने का एक आम तरीका है: http://msdn.microsoft.com/en-us/library/c5kehkcz(VS.71).aspx – mpen

+0

@ मार्क: प्राचीन दस्तावेज़ीकरण का उपयोग करने के बारे में बहुत सावधान रहें। आपका पृष्ठ .NET 1.1 संस्करण था, जब हम बेहतर नहीं जानते थे। Http://msdn.microsoft.com/en-us/library/c5kehkcz.aspx देखें। –

उत्तर

13

चेक बाहर में इस magazine article.

अद्यतन CountdownLatch वर्ग: अब संस्करण 4.0, CountdownEvent class के बाद से ढांचे के अंतर्गत आने वाले।

public class CountdownLatch 
{ 
    private int m_count; 
    private EventWaitHandle m_waitHandle = new EventWaitHandle(true, EventResetMode.ManualReset); 

    public CountdownLatch() 
    { 
    } 

    public void Increment() 
    { 
     int count = Interlocked.Increment(ref m_count); 
     if (count == 1) 
     { 
      m_waitHandle.Reset(); 
     } 
    } 

    public void Add(int value) 
    { 
     int count = Interlocked.Add(ref m_count, value); 
     if (count == value) 
     { 
      m_waitHandle.Reset(); 
     } 
    } 

    public void Decrement() 
    { 
     int count = Interlocked.Decrement(ref m_count); 
     if (m_count == 0) 
     { 
      m_waitHandle.Set(); 
     } 
     else if (count < 0) 
     { 
      throw new InvalidOperationException("Count must be greater than or equal to 0"); 
     } 
    } 

    public void WaitUntilZero() 
    { 
     m_waitHandle.WaitOne(); 
    } 
} 
+7

शुरुआत से खत्म करने के लिए एक काफी अद्भुत लेख। – spender

+0

@ स्पेंडर - आप जो डफी से उम्मीद करेंगे! –

+0

कि 'बोल्डबफर' काफी उपयोगी दिखता है, लेकिन 'काउंटडाउन लॅच' वह नहीं था जिसे मैं ढूंढ रहा था। मैं इसे भी बढ़ाने में सक्षम होना चाहता हूं (जो संकेत को साफ़ करना चाहिए)। – mpen

4

खैर ... आप ब्लॉक करने के लिए मुख्य थ्रेड पीठ पर सब सेमाफोर काउंटरों छीन कर सकते हैं क्रम में जब गिनती 0 है, बजाय गैर शून्य

संशोधित: यहाँ मैं 3 चीजें ग्रहण:

  • कार्यक्रम चल रहा हो, एक नया डाउनलोड काम किसी भी समय शुरू हो सकता है।
  • कार्यक्रम से बाहर निकलने पर, कोई और नया डाउनलोड नहीं होगा जिसकी देखभाल की आवश्यकता है।
  • कार्यक्रम बाहर निकलने पर, आप तो यहाँ

डाउनलोड करने समाप्त करने के लिए सभी फ़ाइलों के लिए प्रतीक्षा करने की आवश्यकता मेरी समाधान, संशोधित है:

एक बड़ा पर्याप्त काउंटर के साथ सेमाफोर आरंभीकृत ताकि आप कभी नहीं प्रवेश अधिकतम (यह केवल 100 या सिर्फ 10 अपनी स्थिति के आधार पर किया जा सकता है):

var maxDownloads = 1000; 
_semaphore = new Semaphore(0, maxDownloads); 
फिर प्रत्येक डाउनलोड पर

, ताकि बाहर निकलने कार्यक्रम की स्थिति में, कोई डाउनलोड शुरू कर सकते हैं डाउनलोड शुरू करने से पहले WaitOne() के साथ शुरू होता । घटना "बाहर निकलें", सेमाफोर पर सभी काउंटरों ऊपर की खपत पर

finally { _semaphore.Release(1); } 

और फिर:

if (_semaphore.WaitOne()) 
    /* proceeds with downloads */ 
else 
    /* we're terminating */ 
डाउनलोड पूरा होने पर

फिर एक काउंटर जारी (अगर हम एक का अधिग्रहण किया था):

for (var i = 0; i < maxDownloads; i++) 
    _semaphore.WaitOne(); 

// all downloads are finished by this point. 

...

+0

मुझे डर है कि मैं काफी समझ नहीं पा रहा हूं। अगर मुझे डाउनलोड की अधिकतम संख्या पता नहीं है तो क्या होगा? और अगर मैंने '_ememahpore.WaitOne();' * पहले * डाउनलोड शुरू किया है, और प्रारंभिक गणना 0 है, तो क्या यह अनिश्चित काल तक प्रतीक्षा नहीं करेगा? – mpen

+0

आपको यह वाक्य अपने प्रश्न में जोड़ना चाहिए: "अगर मुझे डाउनलोड की अधिकतम संख्या पता नहीं है तो क्या होगा?" – chakrit

+0

@ मार्क आप * * को "अवरुद्ध" घटना से पहले एक साथ चलने वाले एसिंक डाउनलोड ऑपरेशन की संख्या जाननी चाहिए, अन्यथा यदि कोई नया डाउनलोड मध्य में या अवरुद्ध घटना के दौरान उभर सकता है, तो आपको एक अलग और जटिल प्रणाली सिर्फ एक साधारण सेमफोर या वाइथंडल्स की तुलना में। आपको उनके संयोजन की आवश्यकता होगी। – chakrit

0

प्रत्येक थ्रेड आप शुरू के लिए इंटरलॉक। काउंटर में वृद्धि करें। और थ्रेड फिनिश पर प्रत्येक कॉलबैक के लिए, इसे कम करें।

फिर थ्रेड के साथ एक लूप करें। नींद (10) या कुछ जब तक गिनती शून्य तक पहुंच जाती है।

+9

असली कार्यक्रम सो नहीं जाते हैं। केवल डेमो करते हैं। –

+0

@ हेनक मुझे लगता है कि आपका मतलब था: असली कार्यक्रम व्यस्त नहीं हैं-प्रतीक्षा करें। केवल डेमो करते हैं। – chakrit

+0

और मुझे सहमत होना है। मेरे हिस्से पर मैला कोड। मुझे @ चक्रित और @ एसएसजी दोनों समाधान पसंद हैं। बीटीडब्ल्यू, किसी के पास जानकारी है कि फ़्रेमवर्क के अंदर प्रतीक्षाऑननेटिव कैसे कार्यान्वित किया जाता है? –

7

लगता System.Threading.WaitHandle.WaitAll की तरह एक बहुत फ़ायदेमंद साबित हो सकता: निर्दिष्ट सरणी में सभी तत्वों के लिए

वेट्स एक संकेत प्राप्त करते हैं।

+0

यह एक व्यवहार्य समाधान की तरह दिखता है, हालांकि यह थोड़ा अनावश्यक रूप से जटिल लगता है ... ऑब्जेक्ट को एक सरणी पर दबाकर और उन्हें अलग-अलग रीसेट करना, जब एक साधारण काउंटर करना चाहिए ... – mpen

10

इन।नेट 4 उस उद्देश्य के लिए CountdownEvent के लिए एक विशेष प्रकार है।

या आप इस तरह अपने आप को समान बात का निर्माण कर सकते हैं:

const int workItemsCount = 10; 
// Set remaining work items count to initial work items count 
int remainingWorkItems = workItemsCount; 

using (var countDownEvent = new ManualResetEvent(false)) 
{ 
    for (int i = 0; i < workItemsCount; i++) 
    { 
     ThreadPool.QueueUserWorkItem(delegate 
             { 
              // Work item body 
              // At the end signal event 
              if (Interlocked.Decrement(ref remainingWorkItems) == 0) 
               countDownEvent.Set(); 
             }); 
    } 
    // Wait for all work items to complete 
    countDownEvent.WaitOne(); 
} 
+1

Aw snap ... कि 'countdownEvent' जब तक मैंने कोशिश नहीं की तब तक बहुत अच्छा लगा। यह आपको 0 पर गिनती शुरू करने और इसे बढ़ाने की अनुमति नहीं देता है क्योंकि यह पहले से ही संकेतित है। – mpen

+0

@ मार्क क्या आप 'उलटी गिनती' के साथ समस्या को और अधिक स्पष्ट रूप से समझा सकते हैं? मुझे इसके साथ समस्या नहीं दिख रही है। – Jez

+0

@ जेज़: जब मैंने टिप्पणी छोड़ दी है, तो यह 3 साल से अधिक हो गया है, लेकिन मेरा मानना ​​है कि मैं 'उलटी गिनती' के साथ होने वाली समस्या यह है कि जैसे ही यह 0 हिट हो जाती है, और मैं इसे 0 पर शुरू करना चाहता हूं और वृद्धि करना चाहता हूं मैंने वस्तुओं को जोड़ा, लेकिन जब तक यह 0 हिट नहीं हो जाता तब तक यह आग नहीं होती है। मुझे लगता है। – mpen

0

यहाँ CountdownLatch की मेरी सी # 2.0 कार्यान्वयन है। गिनती 0 तक प्रतीक्षा करने के अलावा, यदि आप बहुत सारे धागे (गिनती> अधिकतम) उत्पन्न करते हैं तो यह सो जाएगा। चेतावनी: यह पूरी तरह से परीक्षण नहीं किया गया है।

public class ThreadCounter 
{ 
    #region Variables 
    private int currentCount, maxCount; 
    private ManualResetEvent eqZeroEvent; 
    private object instanceLock = new object(); 
    #endregion 

    #region Properties 
    public int CurrentCount 
    { 
     get 
     { 
      return currentCount; 
     } 
     set 
     { 
      lock (instanceLock) 
      { 
       currentCount = value; 
       AdjustZeroEvent(); 
       AdjustMaxEvent(); 
      } 
     } 
    } 

    public int MaxCount 
    { 
     get 
     { 
      return maxCount; 
     } 
     set 
     { 
      lock (instanceLock) 
      { 
       maxCount = value; 
       AdjustMaxEvent(); 
      } 
     } 
    } 
    #endregion 

    #region Constructors 
    public ThreadCounter() : this(0) { } 
    public ThreadCounter(int initialCount) : this(initialCount, int.MaxValue) { } 
    public ThreadCounter(int initialCount, int maximumCount) 
    { 
     currentCount = initialCount; 
     maxCount = maximumCount; 
     eqZeroEvent = currentCount == 0 ? new ManualResetEvent(true) : new ManualResetEvent(false); 
    } 
    #endregion 

    #region Public Methods 
    public void Increment() 
    { 
     ++CurrentCount; 
    } 

    public void Decrement() 
    { 
     --CurrentCount; 
    } 

    public void WaitUntilZero() 
    { 
     eqZeroEvent.WaitOne(); 
    } 
    #endregion 

    #region Private Methods 
    private void AdjustZeroEvent() 
    { 
     if (currentCount == 0) eqZeroEvent.Set(); 
     else eqZeroEvent.Reset(); 
    } 

    private void AdjustMaxEvent() 
    { 
     if (currentCount <= maxCount) Monitor.Pulse(instanceLock); 
     else do { Monitor.Wait(instanceLock); } while (currentCount > maxCount); 
    } 
    #endregion 
} 
0

सुझाव यहाँ के आधार पर, यह है कि क्या मैं के साथ आया है:

1

मैं ने वही समस्या जहां मैं किसी घटना पर एक सर्वर रीसेट करने की जरूरत थी, लेकिन सभी खुले अनुरोध यह हत्या से पहले खत्म करने के लिए के लिए इंतजार करना पड़ा।

मैं 1 के साथ प्रारंभ करने में सर्वर प्रारंभ पर CountdownEvent class इस्तेमाल किया, और प्रत्येक अनुरोध के अंदर मुझे क्या करना:

try 
{ 
    counter.AddCount(); 
    //do request stuff 
} 
finally 
{ 
    counter.Signal(); 
} 

और ResetEvent मैं काउंटर का संकेत मिलने पर एक बार शुरू कर 1 को खत्म करने, और के लिए इंतजार करना सिग्नल करने के लिए लाइव अनुरोध वे कर रहे हैं।

void OnResetEvent() 
{ 
    counter.Signal(); 
    counter.Wait(); 
    ResetServer(); 
    //counter.Reset(); //if you want to reset everything again. 
} 

मूल रूप से आप एक साथ CountdownEvent प्रारंभ, राज्य संकेत इतना है कि यह एक गैर में है, और प्रत्येक AddCount साथ फोन आप काउंटर बढ़ रही हैं, और कॉल प्रत्येक सिग्नल के साथ आप इसे कम हो रही हैं, हमेशा 1 से ऊपर रह। आपके प्रतीक्षा थ्रेड में आप पहले इसे प्रारंभिक 1 मान 0 से कम करने के लिए सिग्नल करते हैं, और यदि कोई धागा चल रहा है तो वे तुरंत अवरुद्ध करना बंद कर देंगे, लेकिन यदि अन्य थ्रेड अभी भी चल रहे हैं, तो प्रतीक्षा थ्रेड तब तक इंतजार करेगा वे संकेत देते हैं। काउंटर 0 हिट होने के बाद देखें, बाद में एडकॉउंट कॉल अपवाद फेंक देंगे, आपको काउंटर को पहले रीसेट करना होगा।

संबंधित मुद्दे