2009-08-27 11 views
17

मैं एक सी # आवेदन लिख रहा हूं। मेरे पास (एक प्रकार का) लॉगिंग क्लास है। और यह लॉगिंग वर्ग कई धागे द्वारा उपयोग किया जाएगा। इस कक्षा धागे को सुरक्षित कैसे करें? क्या मुझे इसे सिंगलटन के रूप में बनाना चाहिए? वहां सर्वोत्तम प्रथाएं क्या हैं? क्या कोई ऐसा दस्तावेज़ है जिसे मैं पढ़ सकता हूं कि इसे थ्रेड-सुरक्षित कैसे बनाया जाए?कक्षा थ्रेड सुरक्षित बनाने के लिए कैसे करें

धन्यवाद

+7

सिंगलटन पैटर्न थ्रेड-सुरक्षा का संकेत नहीं देता है। – dtb

+2

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

+1

जोसेफ अल्बाहारी द्वारा [इस महान लेख] (http://www.albahari.com/threading/part2.aspx#_ThreadSafety) देखें। लेख में लगभग आधा रास्ता "थ्रेड सेफ्टी" –

उत्तर

15

सी # में, किसी भी वस्तु का उपयोग "महत्वपूर्ण खंड" की रक्षा के लिए किया जा सकता है, या दूसरे शब्दों में, कोड जिसे एक ही समय में दो धागे द्वारा निष्पादित नहीं किया जाना चाहिए।

उदाहरण के लिए, निम्नलिखित ShareLogger.Write विधि तक पहुंच सिंक्रनाइज़ कर देगा, इसलिए किसी भी समय किसी भी थ्रेड को एक संदेश लॉगिंग कर रहा है।

public class SharedLogger : ILogger 
{ 
    public static SharedLogger Instance = new SharedLogger(); 

    public void Write(string s) 
    { 
     lock (_lock) 
     { 
     _writer.Write(s); 
     } 
    } 

    private SharedLogger() 
    { 
     _writer = new LogWriter(); 
    } 

    private object _lock; 
    private LogWriter _writer; 
} 
+0

उपर्युक्त कोड एक अच्छा उदाहरण है। – Maciek

+0

यह अच्छा है लेकिन आप इस चीज़ को सिंगलटन बनाने से निपटने के बजाय सिर्फ _lock और _writer स्थिर बना सकते हैं। मौजूदा लॉगर का उपयोग करना अभी भी बहुत आसान होगा। –

+3

मैं मौजूदा लॉगर कार्यान्वयन का उपयोग करने के बारे में सहमत हूं, लेकिन यदि वह बहु-थ्रेडेड कोड में काम कर रहा है, तो उसे अंततः विभिन्न संसाधनों तक पहुंच को सही तरीके से सिंक्रनाइज़ करने के तरीके की आवश्यकता होगी, और इसी प्रक्रिया को अन्यत्र लागू किया जा सकता है ... – jeremyalan

3

उपयोग lock() ताकि एक से अधिक थ्रेड एक ही समय

6
  • कोशिश में प्रवेश का उपयोग नहीं होगा और स्थानीय चर का उपयोग कर सबसे गणना करते हैं और उसके बाद में वस्तु की स्थिति में परिवर्तन एक त्वरित lock एड ब्लॉक
  • ध्यान रखें कि जब आप उन्हें पढ़ते हैं और जब आप राज्य को बदलते हैं तो कुछ चर बदल सकते हैं।
+0

+1 पर दूसरी पंक्ति को ध्यान में रखने के लिए एक महान खंड है, मैंने 2weeks को समझ लिया कि –

9

मैं इसके लिए शेल्फ लॉगर का उपयोग करता हूं, क्योंकि ऐसे कई हैं जो रॉक ठोस और उपयोग करने में आसान हैं। खुद को रोल करने की जरूरत नहीं है। मैं Log4Net.

+0

लॉग 4नेट बहुत अच्छा है, मैंने इसे कई बार उपयोग किया है और हमेशा खुश हूं यह। –

+10

मैं लॉगिंग के संबंध में इसके साथ सहमत हूं। लेकिन यह सवाल का जवाब नहीं देता है। –

+0

Log4Net shiznit है! – Crackerjack

2

BCS 'जवाब के अनुसार:

BCS एक राज्यविहीन वस्तु के मामले का वर्णन किया गया है। इस तरह की एक वस्तु आंतरिक रूप से थ्रेड सुरक्षित है क्योंकि इसमें स्वयं के कोई चर नहीं हैं जिन्हें अलग-अलग पैरों से कॉल करके गिरफ्तार किया जा सकता है।

वर्णित लॉगर में फ़ाइल हैंडल है (क्षमा करें, सी # उपयोगकर्ता नहीं, शायद इसे आईडीस्कफाइल रिसोर्स या कुछ ऐसे एमएस-आईएसएम कहा जाता है) जिसे इसे क्रमबद्ध करना होगा।

तो, लॉग फ़ाइल में लिखने वाले तर्क से संदेशों का संग्रहण अलग करें। तर्क केवल एक ही समय में एक संदेश पर काम करना चाहिए।

ऐसा करने का एक तरीका यह है: यदि लॉगर ऑब्जेक्ट संदेश ऑब्जेक्ट्स की कतार रखना था, और लॉगर ऑब्जेक्ट में केवल कतार से एक संदेश पॉप करने के लिए तर्क है, तो किसी संदेश ऑब्जेक्ट से उपयोगी सामग्री निकालें, फिर इसे लॉग में लिखें, फिर कतार में एक और संदेश देखें - फिर आप कतार के जोड़/निकालने/queue_size/आदि संचालन थ्रेड को सुरक्षित करके उस धागे को सुरक्षित बना सकते हैं। इसके लिए लॉगर क्लास, एक संदेश वर्ग और थ्रेड सुरक्षित कतार की आवश्यकता होगी (जो शायद एक तीसरी कक्षा है, जिसका एक उदाहरण लॉगर क्लास का सदस्य चर है)।

+0

एक और विकल्प (कुछ मामलों में) लॉगर कॉल स्थानीय मेमोरी में पूर्ण रिकॉर्ड उत्पन्न करना है और फिर इसे एकल कॉल में आउटपुट स्ट्रीम में लिखना है। आईआईआरसी अधिकांश ओएस एक सिस्टम कॉल प्रदान करते हैं जो किसी भी मामले (लगभग?) के लिए एक धारा पर एक परमाणु लेखन करेगा, लेकिन संसाधन थकावट, और फिर आपको अन्य समस्याएं हैं। – BCS

8

मुझे यकीन नहीं है कि लॉगिंग क्लास थ्रेड-सुरक्षित बनाने के बारे में पहले से ही क्या कहा जा रहा है, मैं कुछ भी जोड़ सकता हूं। जैसा कि कहा गया है, ऐसा करने के लिए आपको संसाधन तक पहुंच को सिंक्रनाइज़ करना होगा, यानी लॉग फ़ाइल, ताकि एक ही थ्रेड एक समय में लॉग इन करने का प्रयास कर सके। C# lock कीवर्ड ऐसा करने का उचित तरीका है।

हालांकि, मैं सिंगलटन दृष्टिकोण (1) को संबोधित करूंगा और (2) उस दृष्टिकोण की उपयोगिता जिसे आप आखिरकार उपयोग करने का निर्णय लेते हैं।

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

(2) दृष्टिकोण की उपयोगिता का सवाल है, यह सुझाव दिया समाधान पर विचार:

public class SharedLogger : ILogger 
{ 
    public static SharedLogger Instance = new SharedLogger(); 
    public void Write(string s) 
    { 
     lock (_lock) 
     { 
     _writer.Write(s); 
     } 
    } 
    private SharedLogger() 
    { 
     _writer = new LogWriter(); 
    } 
    private object _lock; 
    private LogWriter _writer; 
} 

मुझे पहले का कहना है कि इस दृष्टिकोण आम तौर पर ठीक है चलो। यह के एक सिंगलटन उदाहरण को Instance स्थिर चर के माध्यम से परिभाषित करता है और अन्य को निजी कन्स्ट्रक्टर के आधार पर कक्षा को तुरंत चालू करने से रोकता है। यह सिंगलटन पैटर्न का सार है, लेकिन मैं बहुत दूर जाने से पहले singletons in C# के संबंध में जॉन स्कीट की सलाह पढ़ने और पालन करने की दृढ़ता से अनुशंसा करता हूं।

हालांकि, मैं इस समाधान की उपयोगिता पर ध्यान केंद्रित करना चाहता हूं। 'प्रयोज्यता' से, मैं इस संदेश का लॉग इन करने के लिए इस कार्यान्वयन का उपयोग करने के तरीके का जिक्र कर रहा हूं। पर विचार करें मंगलाचरण कैसा दिखता है:

SharedLogger.Instance.Write("log message"); 

पूरे 'उदाहरण' भाग सिर्फ गलत लग रहा है, लेकिन वहाँ कोई रास्ता नहीं यह कार्यान्वयन दिया से बचने के लिए है यही कारण है कि।

public static class SharedLogger : ILogger 
{ 
    private static LogWriter _writer = new LogWriter(); 
    private static object _lock = new object(); 
    public static void Write(string s) 
    { 
     lock (_lock) 
     { 
      _writer.Write(s); 
     } 
    } 
} 

सूचना उस वर्ग अब स्थिर है, जिसका अर्थ है अपने सदस्यों और तरीकों के सभी स्थिर होना जरूरी है कि: इसके बजाय, इस विकल्प पर विचार करें। यह पहले के उदाहरण से काफी अलग नहीं है, लेकिन इसके उपयोग पर विचार करें।

SharedLogger.Write("log message"); 

यह कोड के खिलाफ कोड करना बहुत आसान है।

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

+0

मैं इसके साथ सहमत हूं ... बहुत कम बकवास। –

+0

अंगूठे के नियम के रूप में जहां भी संभव हो स्टेटिक कक्षाएं। –

+0

क्या हम लॉक (_this) के बजाय लॉक (यह) का उपयोग नहीं कर सकते हैं? (लॉक करने के लिए ऑब्जेक्ट के रूप में तत्काल उपयोग करना?) –

1

मेरी राय में उपर्युक्त प्रदान किया गया कोड अब थ्रेड सुरक्षित नहीं है: पिछले समाधान में आपको ShareLogger की एक नई वस्तु को स्थापित करना था और विधि प्रत्येक ऑब्जेक्ट के लिए एक बार अस्तित्व में थी।

धागा 1: SharedLogger.Write ("थ्रेड 1")

धागा 2: SharedLogger

अब आप सिर्फ एक लिखें विधि है, जो सभी धागे द्वारा किया जाता है, उदाहरण के लिए है।लिखें ("थ्रेड 2");

public static void Write(string s) 
    { 
     // thread 1 is interrupted here <= 
     lock (_lock) 
     { 
      _writer.Write(s); 
     } 
    } 
  • धागा 1 संदेश लिखने के लिए चाहता है, लेकिन धागा 2 (टिप्पणी)
  • धागा 2 ओवरराइड धागा 1 के संदेश द्वारा बाधित है और धागे से परेशान हो जाता 1

  • थ्रेड 1 लॉक हो जाता है और लिखता है "थ्रेड 2"

  • थ्रेड 1 लॉक
  • थ्रेड 2 लॉक हो जाता है और लिखता है "थ्रेड 2 "
  • धागा 2 रिलीज ताला

मुझे सही करें जब मैं गलत हूँ ...

0

यदि प्रदर्शन नहीं है 'मुख्य मुद्दा, उदाहरण के लिए यदि वर्ग नहीं एक बहुत किया जा रहा है लोड की, बस इस कार्य करें:

अपनी कक्षा ContextBoundObject

वारिस को अपनी कक्षा में इस विशेषता को लागू करें [तुल्यकालन]

आपकी पूरी कक्षा अब एक समय में केवल एक धागे तक पहुंच योग्य है।

यह निदान के लिए वास्तव में अधिक उपयोगी है, गति के रूप में यह लगभग सबसे खराब मामला है ... लेकिन जल्दी से यह निर्धारित करने के लिए "यह अजीब मुद्दा रेसिंग हालत है", इसे फेंक दो, परीक्षण चलाएं .. अगर समस्या हो जाती है दूर ... आप जानते हैं कि यह एक थ्रेडिंग समस्या है ...

एक और अधिक प्रदर्शन विकल्प है कि आपके लॉगिंग क्लास में थ्रेड सुरक्षित संदेश कतार है (जो संदेश लॉगिंग स्वीकार करता है, फिर उन्हें बाहर खींचें और अनुक्रमिक रूप से संसाधित करें .. ।

उदाहरण के लिए नई समानांतरवाद सामान में ConcurrentQueue वर्ग एक अच्छा धागा सुरक्षित कतार है।

या उपयोगकर्ता log4net RollingLogFileAppender, जो पहले से ही thread safe है।

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