2011-07-29 18 views
9

मल्टीथ्रेड प्रोग्रामिंग के बारे में जानने के लिए बहुत कुछ लगता है और यह सब कुछ डरावना है।एक पूरी विधि थ्रेड-सुरक्षित बनाने का सबसे आसान तरीका?

मेरे वर्तमान आवश्यकताओं के लिए, मैं सिर्फ एक विधि के खिलाफ की रक्षा करने के लिए एक और धागा से फिर से बुलाया जा रहा है इससे पहले कि यह खत्म चाहते हैं, और मेरे सवाल यह है:

इस है एक पर्याप्त (सुरक्षित) जिस तरह से एक बनाने के लिए विधि धागा-सुरक्षित?

class Foo 
{ 
    bool doingWork; 
    void DoWork() 
    { 
     if (doingWork) // <- sophistocated thread-safety 
      return;  // <- 

     doingWork = true; 

     try 
     { 
      [do work here] 
     } 
     finally 
     { 
      doingWork = false; 
     } 
    } 
} 

अगर वह पर्याप्त नहीं है, क्या सबसे आसान तरीका है इस लक्ष्य को हासिल करने के लिए है?


संपादित करें: परिदृश्य के बारे में अधिक जानकारी:

  • फू उसमें केवल एक ही नहीं है

  • Foo.DoWork() पर एक ThreadPool धागे से बुलाया जाएगा सिस्टम की घटना की घटना समाप्त हो गई। टाइमर। टाइमर।

  • आम तौर पर Foo.DoWork() अगली बार कहा जाता है से पहले युगों खत्म हो जाएगा, लेकिन मैं पतली संभावना है कि यह लंबे समय चलेंगे, और समाप्त करने से पहले फिर से बुलाया पाने के लिए कोड करना चाहते हैं।


(मैं भी बहुत चालाक यकीन है कि इस सवाल का भाषा-नास्तिक टैग किया जा सकता है अगर होना करने के लिए नहीं कर रहा हूँ, इसलिए मैं नहीं किया है। प्रबुद्ध पाठकों, इसलिए यदि लागू करने के लिए स्वतंत्र महसूस हो रहा है।)

+0

क्या प्रत्येक थ्रेड के लिए फू तत्काल प्रकार का ऑब्जेक्ट है, या इसे एकाधिक धागे में साझा किया गया है? – NotMe

+0

डॉकवर्क विधि को कॉल करने वाले कोड के बारे में अधिक जानकारी दें, क्या कई धागे हैं? – sll

+0

या तो फिर से प्रवेश करें या अपना कोड डिज़ाइन करें ताकि ऐसा न हो।जब ऐसा होता है तो बस बाहर निकलना सही समाधान होने की संभावना नहीं है। लेकिन आपका संपादन यह स्पष्ट करता है कि थ्रेड सुरक्षा वास्तव में मुद्दा है कि फिर से प्रवेश। –

उत्तर

8

आपका कोड थ्रेड सुरक्षित नहीं है। आपको इसके बजाय lock कीवर्ड का उपयोग करना चाहिए।

अपने वर्तमान कोड में:

if (doingWork) 
     return; 

    // A thread having entered the function was suspended here by the scheduler. 

    doingWork = true; 

अगले धागा के माध्यम से आती है, यह भी समारोह में प्रवेश करेंगे।

यही कारण है कि lock निर्माण का उपयोग किया जाना चाहिए। यह मूल रूप से अपने कोड के रूप में ही है, लेकिन एक धागा के लिए जोखिम के बिना बीच में बाधित किया जा रहा:

class Foo 
{ 
    object lockObject = new object; 
    void DoWork() 
    { 
     lock(lockObject) 
     { 
      [do work here] 
     } 
    } 
} 

ध्यान दें कि यह कोड अपने मूल से कुछ भिन्न अर्थ विज्ञान है। यह कोड दूसरे थ्रेड को प्रतीक्षा करने के लिए प्रवेश करेगा और फिर काम करेगा। आपके मूल कोड ने दूसरा थ्रेड बनाया है। अपने मूल कोड के करीब आने के लिए, C# lock कथन का उपयोग नहीं किया जा सकता है। अंतर्निहित Monitor निर्माण सीधे इस्तेमाल किया जा करने के लिए है: यदि आप आसान कोड चाहते हैं और प्रदर्शन के बारे में ध्यान न बहुत ज्यादा यह हो सकता है

class Foo 
{ 
    object lockObject = new object; 
    void DoWork() 
    { 
     if(Monitor.TryEnter(lockObject)) 
     { 
      try 
      { 
       [do work here] 
      } 
      finally 
      { 
       Monitor.Exit(lockObject); 
      } 
     } 
    } 
} 
+0

मुझे वास्तव में निरस्त करने की आवश्यकता नहीं है, इसलिए लॉक विधि ठीक होनी चाहिए। मीठा - यह आसान है! धन्यवाद। –

+0

[वैलेंटाइन] (http://stackoverflow.com/questions/6879468/simplest-way-to-make-a-whole-method-thread-safe/6879698#6879698) केवल लॉक के साथ निरस्त करने का एक तरीका है ()। इस पर आपकी टिप्पणियों में रूचि है। –

0

http://msdn.microsoft.com/en-us/library/system.threading.barrier.aspx

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

+0

यह मेरे लिए अत्यधिक जटिल लगता है। मैं वास्तव में केवल यह प्राप्त करना चाहता हूं कि मेरा कोड स्निपेट क्या तात्पर्य है: पुन: प्रस्तुत होने पर बाहर निकलें। –

+0

बाधा में प्रतिभागी गणना की जांच करें, अगर यह> 0 वापसी है तो प्रतिभागी जोड़ें और काम करें, अंत में प्रतिभागी को हटा दें। बैरियर थ्रेड सुरक्षित होने के लिए है। –

+0

क्या आप चेक कर सकते हैं और एक परमाणु तरीके से प्रतिभागी जोड़ सकते हैं? यदि हां, तो कैसे? –

3

पुन: प्रवेश के बहु-थ्रेडिंग के साथ कुछ लेना देना नहीं है।

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

आपका कोड उसी ऑब्जेक्ट उदाहरण के भीतर पुन: प्रवेश से सुरक्षित है, जब तक कि सब कुछ एक ही थ्रेड पर हो।

[do work here] जब तक बाहरी कोड चलाने में सक्षम नहीं है (उदाहरण के लिए, किसी ईवेंट को उठाकर, या किसी प्रतिनिधि से किसी प्रतिनिधि या विधि को कॉल करके), यह पहली जगह में फिर से प्रवेश नहीं कर रहा है।

आपका संपादित प्रश्न इंगित करता है कि यह संपूर्ण अनुभाग आपके लिए अप्रासंगिक है।
आपको शायद इसे वैसे भी पढ़ना चाहिए।


आप हो सकता है (संपादित: कर रहे हैं) विशिष्टता – सुनिश्चित करना है कि विधि दो बार एक ही बार में अगर एक साथ कई धागे से बुलाया नहीं चलेगा की तलाश में।
आपका कोड अनन्य नहीं है। यदि दो थ्रेड एक बार में विधि चलाते हैं, और वे दोनों if कथन एक बार में चलाते हैं, तो वे दोनों if से पहले प्राप्त करेंगे, फिर दोनों doingWork ध्वज सेट करेंगे, और दोनों संपूर्ण विधि को चलाएंगे।

ऐसा करने के लिए, lock कीवर्ड का उपयोग करें।

+0

वैसे यह टैग में बहुसंख्यक कहता है तो कोई केवल मान सकता है। –

+1

कोई केवल _what_ मान सकता है? प्रश्न में बहु-थ्रेडिंग के साथ कुछ लेना देना नहीं है। ** संपादित करें **: अब, यह करता है। – SLaks

+0

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

2

के रूप में आसान

class Foo 
{ 
    bool doingWork; 
object m_lock=new object(); 
    void DoWork() 
    { 
     lock(m_lock) // <- not sophistocated multithread protection 
{ 
     if (doingWork) 
      return;  
     doingWork = true; 
} 


     try 
     { 
      [do work here] 
     } 
     finally 
     { 
lock(m_lock) //<- not sophistocated multithread protection 
{ 
      doingWork = false; 
} 
     } 
    } 

के रूप में}

हैं आप थोड़ा लॉक करना चाहते हैं, आप एक ऐसी संपत्ति बना सकते हैं जो थ्रेड सुरक्षित है:

public bool DoingWork 
{ 
get{ lock(m_Lock){ return doingWork;}} 
set{lock(m_lock){doingWork=value;}} 
} 

अब आप इसे इसके बजाय फ़ील्ड का उपयोग कर सकते हैं, हालांकि इसके परिणामस्वरूप लॉक उपयोगों की बढ़ती संख्या को लॉक करने के लिए अधिक समय व्यतीत होगा।

या आप पूर्ण बाड़ दृष्टिकोण (महान सूत्रण पुस्तक Joseph Albahari online threading से) का उपयोग कर सकते

class Foo 
{ 
    int _answer; 
    bool _complete; 

    void A() 
    { 
    _answer = 123; 
    Thread.MemoryBarrier(); // Barrier 1 
    _complete = true; 
    Thread.MemoryBarrier(); // Barrier 2 
    } 

    void B() 
    { 
    Thread.MemoryBarrier(); // Barrier 3 
    if (_complete) 
    { 
     Thread.MemoryBarrier();  // Barrier 4 
     Console.WriteLine (_answer); 
    } 
    } 
} 

उन्होंने कहा कि पूर्ण बाड़ 2x तेजी से ताला बयान से है। कुछ मामलों में आप MemoryBarrier() को अनइडेड कॉल को हटाकर प्रदर्शन में सुधार कर सकते हैं, लेकिन lock का उपयोग करना सरल, अधिक स्पष्ट और कम त्रुटि प्रवण है।

मेरा मानना ​​है कि यह int आधारित doWork फ़ील्ड के आसपास Interlocked कक्षा का उपयोग करके भी किया जा सकता है।

+0

क्या आपके लॉक() (डिकवर्क्स तक पहुंच लॉक करना) और एंडर्स लॉक() विधि (काम करने वाले कोड तक पहुंच लॉक करने) के बीच कोई उल्लेखनीय अंतर है? –

+0

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

+0

ओह, मैं देखता हूं, अच्छा। यह कैसा है, एंडर्स? :) –