2012-05-04 7 views
5

एक बहुत ही बुनियादी लकड़हारा के लिए इस कोड के साथ:इस कोड में लॉक क्यों काम नहीं कर रहा है?

The process can't access the file because its being used by another file. 

क्यों लॉक का उपयोग करने के धागे को रोकने नहीं है:

lock (string.Concat("LogWritter_", this.FileName)) 
{ 
    using (var fileStream = File.Open(this.FileName, FileMode.Append, FileAccess.Write, FileShare.Read)) 
    { 
     using (var w = new StreamWriter(fileStream)) 
     { 
      w.Write(message); 
     } 
    } 
} 

जब मैं कुछ धागे से यह कोशिश एक साथ मैं जल्दी से त्रुटि मिलती है एक ही समय में फाइल?

इससे कोई फर्क नहीं पड़ता कि थ्रेड एक ही उदाहरण या एक ही फ़ाइल में अलग-अलग उदाहरणों को कॉल करते हैं। मैंने सोचा कि यह विंडोज़ में फाइलें लिखते समय कुछ स्थगित होने के कारण हो सकता है, लेकिन लिनक्स पर भी वही होता है।

+1

हैश कोड! = संदर्भ। 'संदर्भ एक्वाल्स' का प्रयोग करें। –

+1

हैश कोड! = संदर्भ – TheBuzzSaw

+0

हैश कोड स्ट्रिंग के * सामग्री * का एक पाचन है। संदर्भ उस विशेष उदाहरण के बारे में विवरण रखता है। कंपाइलर में समान सामग्री वाले हार्ड-कोडेड तारों को स्वचालित रूप से संयोजित करने के विकल्प होते हैं, लेकिन रन-टाइम कुछ तारों को गठबंधन करने के लिए रेट्रो-तुलनाओं के टन नहीं करता है। – TheBuzzSaw

उत्तर

12

आप एक अस्थायी स्ट्रिंग पर लॉक कर रहे हैं। आपको लॉक करने के लिए एक स्थिर ऑब्जेक्ट पेश करना होगा।

+2

कंधे ' स्ट्रिंग केवल अपरिवर्तनीय/संग्रहीत हो सकती है इससे कोई फर्क नहीं पड़ता कि यह कितनी बार बनाया गया है? – zimdanen

+0

अपरिवर्तनीयता क्यों प्रासंगिक है? – spender

+0

@zimdanen बनाए गए कई अलग-अलग तारों में से प्रत्येक अपरिवर्तनीय है। उत्परिवर्तन समस्या नहीं है। –

8

Dictionary<string,object> बनाएं और अपनी लॉक ऑब्जेक्ट्स को फ़ाइलपैथ के साथ कुंजी के रूप में स्टोर करें।

Locking by string. Is this safe/sane?

+2

_syncRoot' हालांकि इस सवाल का जवाब कुछ संदर्भ याद करते हैं, इस यह करने के लिए यदि आप एक से अधिक फ़ाइलों के लिए एक एकल ताला नहीं करना चाहती सही तरीका है। कुछ [लॉगिंग ढांचा] में से [इस कार्यान्वयन] (http://logging.codeplex.com/SourceControl/changeset/view/72677#1298869) में उदाहरण के लिए देखो (http://logging.codeplex.com)।यह कार्यान्वयन 'ऑर्डिनल इग्नोरकेस' 'डिक्शनरी' का उपयोग करता है और यह सुनिश्चित करता है कि कैनोलिक फ़ाइल पथ का उपयोग किया जाता है (महत्वपूर्ण)। – Steven

+0

@ स्पेंडर, जो मैंने उपयोग किया था और अच्छी तरह से काम करता है –

+0

@Steven, –

4

आप केवल एक डायनामिक रूप से तैयार स्ट्रिंग ("LogWritter_" + this.FileName) ताला लगा रहे हैं:

कुछ समय पहले, मैं इस एक ही सवाल का दरवाजा खटखटाया! प्रत्येक धागा एक और बना देगा। एक आम लॉक-वस्तु के बजाय

public static readonly object fileLock = new object(); 

... 

lock (fileLock) { 
    ... 
} 

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

यदि आप .NET Framework 4.0 के साथ काम कर रहे हैं, तो आप ConcurrentDictionary<TKey, TValue> का उपयोग कर सकते हैं। नहीं तो आप एक सामान्य Dictionary<TKey, TValue>

के लिए उपयोग लॉक करना होगा
public static readonly ConcurrentDictionary<string,object> fileLocks = 
    new ConcurrentDictionary<string,object>(); 

... 

object lockObject = fileLocks.GetOrAdd(filename, k => new object()); 
lock (lockObject) { 
    ... 
} 

अद्यतन

आप दो तार के संदर्भ की तुलना करना चाहते हैं, तो आप

Object.ReferenceEquals(s1, s2) 
उपयोग करने के लिए

जहां

string s1 = "Hello"; 
string s2 = "Hello"; 
Console.WriteLine(Object.ReferenceEquals(s1, s2)); // ===> true 

string s3 = s1 + " World!"; 
string s4 = s2 + " World!"; 
Console.WriteLine(s3 == s4); // ===> true 
Console.WriteLine(Object.ReferenceEquals(s3, s4)); // ===> false 

संकलन-समय पर बनाए गए तारों को प्रशिक्षित किया जाता है, यानी समान स्ट्रिंग के लिए एकल स्ट्रिंग स्थिरता बनाई जाएगी। रनटाइम पर बनाए गए तार, हालांकि, व्यक्तिगत और विशिष्ट वस्तुओं के रूप में बनाए जाएंगे!

स्ट्रिंग के हैश कोड की गणना उनके संदर्भ से नहीं, स्ट्रिंग के पात्रों से की जाती है।

+0

लिंक के लिए आपको बहुत बहुत धन्यवाद, मुझे लगता है कि आपका मतलब है 'सार्वजनिक स्थैतिक रीडोनली ऑब्जेक्ट फ़ाइल लॉक' :) – TheBuzzSaw

+0

हाँ, इसे ठीक किया गया। –

4

C# lock statement स्ट्रिंग की विशिष्टता नहीं, ऑब्जेक्ट पर लॉक रखता है। इसलिए, क्योंकि आप दो तारों को गतिशील रूप से संयोजित कर रहे हैं, इसलिए आप अनिवार्य रूप से हर बार एक नई वस्तु बना रहे हैं, इसलिए प्रत्येक एकल ताला अद्वितीय है। यहां तक ​​कि यदि आप हर बार एक ही फ़ाइल तक पहुंचते हैं, तो "ए" + "बी" कुछ नई अपरिवर्तनीय स्ट्रिंग में परिणाम देता है; "ए" + "बी" फिर से एक और नई वस्तु में परिणाम।

1

इस कोड को आज़माएं।जब पहली बार धागा में आता है और string.Concat का मूल्यांकन ("LogWritter_", this.FileName) यह एक ताला इस स्ट्रिंग पर डालता है। दूसरा धागा भी उसी स्ट्रिंग मान की गणना करेगा लेकिन तार अलग-अलग होंगे। यदि आप ==, बराबर() या GetHashCode() का उपयोग करके तारों की तुलना करेंगे, तो आप देखेंगे कि दोनों तार समान हैं क्योंकि स्ट्रिंग क्लास के लिए == और बराबर() ओवरलोड किए गए हैं। लेकिन यदि आप ReferenceEquals() की जांच करेंगे तो आप झूठी वापसी करेंगे। इसका मतलब है कि दोनों स्ट्रिंग के अलग-अलग संदर्भ हैं। और यही कारण है कि पहली स्ट्रिंग ऑब्जेक्ट पर दूसरी थ्रेड लॉक और दूसरे स्ट्रिंग ऑब्जेक्ट पर दूसरे थ्रेड लॉक और आपको त्रुटि मिलती है।

class Program 
{ 
    public static void Main(string[] args) 
    { 
     string locker = "str", temp = "temp"; 
     string locker1 = locker + temp; 
     string locker2 = locker + temp; 

     Console.WriteLine("HashCode{0} {1}", locker1.GetHashCode(), locker2.GetHashCode()); 
     Console.WriteLine("Equals {0}", locker1.Equals(locker2)); 
     Console.WriteLine("== {0}", locker1 == locker2); 
     Console.WriteLine("ReferenceEquals {0}", ReferenceEquals(locker1, locker2)); 
     app.Program p = new Program(); 
     Action<string> threadCall = p.Run; 
     threadCall.BeginInvoke(locker1, null, null); 
     threadCall.BeginInvoke(locker2, null, null); 
     Console.Read(); 
    } 

    public void Run(string str) 
    { 
     lock (str) 
     { 
      Console.WriteLine("im in"); 
      Thread.Sleep(4000); 
      Console.WriteLine("print from thread id {0}", Thread.CurrentThread.ManagedThreadId); 
     } 
    } 


}