2015-10-07 8 views
6

को देखते हुए:ThreadLocks और स्टेटिक कंस्ट्रक्टर्स

एक ASP.net वेब एपीआई आवेदन IIS में की मेजबानी की। एप्लिकेशन प्लगइन के लिए प्रत्येक के बारे में 30 ऐप डोमेन बनाता है जो कुछ बाहरी काम करता है।

एप्लिकेशन बहुत सारे उपयोगकर्ताओं की सेवा करता है और अधिकतर समय बहुत अच्छी तरह से चलता है, लेकिन कुछ बार (दिन या यहां तक ​​कि सप्ताहों के बाद) यह अचानक लटकता है।

समस्या:

एक WebApplication कभी कभी है "रुक जाता है" जो w3wp.exe को पुनः आरंभ करने की माँग का कारण।

इस स्थिति में डंप की कुछ परीक्षाओं के बाद हमने पाया कि इस क्षण में बहुत सारे धागे (कभी-कभी लगभग 15.000) होते हैं।

सामान्य स्थिति में हम कभी भी एक सौ धागे का निरीक्षण नहीं करते हैं।

DebugDiag कहते हैं एक धागा दूसरों

enter image description here

अब हमने देखा है कि धागा 44 (और कई कई अन्य लोगों, के बारे में 90%) में अवरुद्ध होता है, अंत में एक ही फोन है :

enter image description here

विधि ही किसी भी ताला या सूत्रण behavior.but यह अपने स्थिर निर्माता के विषय में एक असामान्य बात है नहीं है। ctor इस तरह दिखता है:

static TimeZoneHelper() 
     { 
     using (StringReader reader = new StringReader(Resources.TimeZones)) 
     { 
      string line; 

      while ((line = reader.ReadLine()) != null) 
      { 
       string[] parts = line.Split(';'); 

       TimeZoneInfo timeZone = TimeZoneInfo.FindSystemTimeZoneById(parts[1]); 

       timeZones[parts[0]] = timeZone; 
      } 
     } 
} 

इसके अलावा डिबग विश्लेषण दर्शाता है कि आवेदन एक सक्रिय जीसी में था (और आप संभावित पूछना होगा के रूप में: हम कभी भी स्वयं एक gc.collect प्रदर्शित नहीं) enter image description here

प्रश्न क्या कोई सबूत बताता है कि इस प्रकार का कोड एक स्थिर ctor में समस्याग्रस्त है? भले ही कार्य या थ्रेडिंग कोड न हो? शायद जीसी प्रगति खुद से संबंधित कुछ मैं एक सार ctor inlcuding इस वर्ग के मुख्य तरीके से युक्त बनाया

TimeZoneHelper (के रूप में वस्तु डिस्पोजेबल, यहां तक ​​कि एक निपटाने कोड नहीं होने तो क्या होगा?) है और विधि है जिसके TimeZoneHelper.ToTimeZoneOffset बुलाया गया था: में

https://gist.github.com/Gentlehag/9d564555261da0e73366

मुख्य बात विधि परिणाम एक Dictionary.TryGet (ctor में बनाया गया था कौन सा)

है 0

संपादित करें बीटीडब्ल्यू मैं प्रत्येक एपडोमेन में भी जोड़ना चाहता हूं, एक असेंबली समाधान घटना बाध्य है। कोड यहाँ देखा जा सकता:

https://gist.github.com/Gentlehag/4726b6d888adb149684d


महत्वपूर्ण अपडेट मैं एक सहयोगी हूँ और अभी कुछ और जानकारी जोड़ना चाहते हैं। हमें एक और परिदृश्य भी मिला जो बहुत समान है। मैं धागा कि ब्लॉक का मालिक से स्टैकट्रेस है:

000000c898897560 00007ff8855b7e5d System.Collections.Generic.Dictionary`2[[System.__Canon, mscorlib],[System.__Canon, mscorlib]].FindEntry(System.__Canon) 
000000c8988975d0 00007ff8855b7d34 System.Collections.Generic.Dictionary`2[[System.__Canon, mscorlib],[System.__Canon, mscorlib]].TryGetValue(System.__Canon, System.__Canon ByRef) 
000000c898897610 00007ff88f6152b3 GP.Components.Extensions.AppDomains.RemotingRunner.CurrentDomain_AssemblyResolve(System.Object, System.ResolveEventArgs) 
000000c8988978a0 00007ff886f7276c System.AppDomain.OnAssemblyResolveEvent(System.Reflection.RuntimeAssembly, System.String) 
000000c898897bd0 00007ff8e4b2a7f3 [GCFrame: 000000c898897bd0] 
000000c898899b78 00007ff8e4b2a7f3 [HelperMethodFrame_PROTECTOBJ: 000000c898899b78] System.Reflection.RuntimeAssembly._nLoad(System.Reflection.AssemblyName, System.String, System.Security.Policy.Evidence, System.Reflection.RuntimeAssembly, System.Threading.StackCrawlMark ByRef, IntPtr, Boolean, Boolean, Boolean) 
000000c898899c80 00007ff886f7224e System.Reflection.RuntimeAssembly.InternalGetSatelliteAssembly(System.String, System.Globalization.CultureInfo, System.Version, Boolean, System.Threading.StackCrawlMark ByRef) 
000000c898899d60 00007ff886f716c8 System.Resources.ManifestBasedResourceGroveler.GetSatelliteAssembly(System.Globalization.CultureInfo, System.Threading.StackCrawlMark ByRef) 
000000c898899df0 00007ff885b932fb System.Resources.ManifestBasedResourceGroveler.GrovelForResourceSet(System.Globalization.CultureInfo, System.Collections.Generic.Dictionary`2, Boolean, Boolean, System.Threading.StackCrawlMark ByRef) 
000000c898899eb0 00007ff885b92ecb System.Resources.ResourceManager.InternalGetResourceSet(System.Globalization.CultureInfo, Boolean, Boolean, System.Threading.StackCrawlMark ByRef) 
000000c898899fa0 00007ff885b92b73 System.Resources.ResourceManager.InternalGetResourceSet(System.Globalization.CultureInfo, Boolean, Boolean) 
000000c898899ff0 00007ff885b92014 System.Resources.ResourceManager.GetString(System.String, System.Globalization.CultureInfo) 
000000c89889a0a0 00007ff89914aa62 NewRelic.Agent.Core.Config.ConfigurationLoader.InitializeFromXml(System.String, System.String) 
000000c89889a140 00007ff89914a838 NewRelic.Agent.Core.Config.ConfigurationLoader.Initialize(System.String) 
000000c89889a1a0 00007ff899143be9 NewRelic.Agent.Core.Config.ConfigurationLoader.Initialize() 
000000c89889a210 00007ff899123a27 NewRelic.Agent.Core.Agent+AgentSingleton.CreateInstance() 
000000c89889a280 00007ff8991239c2 NewRelic.Agent.Core.Singleton`1[[System.__Canon, mscorlib]]..ctor(System.__Canon) 
000000c89889a2c0 00007ff89912388b NewRelic.Agent.Core.Agent..cctor() 
000000c89889a700 00007ff8e4b2a7f3 [GCFrame: 000000c89889a700] 
000000c89889ce88 00007ff8e4b2a7f3 [PrestubMethodFrame: 000000c89889ce88] NewRelic.Agent.Core.Agent.get_Instance() 
000000c89889cef0 00007ff89912358c NewRelic.Agent.Core.AgentShim.GetTracer(System.String, UInt32, System.String, System.String, System.Type, System.String, System.String, System.String, System.Object, System.Object[]) 
000000c89889d280 00007ff8e4b2a7f3 [DebuggerU2MCatchHandlerFrame: 000000c89889d280] 

यह TimeZoneHelper वर्ग के बारे में नहीं है, लेकिन यह दिलचस्प एक आम पहलू है कि वहाँ है: दोनों वर्ग भार उनके स्थिर निर्माता में एक संसाधन (या तो न्यूज़ेलिक या टाइमज़ोन के साथ फ़ाइल के लिए कॉन्फ़िगरेशन फ़ाइल)। तो परिदृश्य प्रतीत हो रहा है निम्नलिखित:

  1. एकाधिक धागे वर्ग
  2. पहले धागा स्थिर निर्माता के लिए ताला हो जाता है का उपयोग करने के लिए प्रयास करें और इस निर्माता
  3. एक संसाधन भरी हुई है और नेट चलाता है रनटाइम संसाधन असेंबली लोड करने का प्रयास करता है।
  4. हम संसाधन असेंबली लोड करने और किसी तरह से डेडलॉक का कारण बनने के लिए असेंबली रिसोलव-इवेंट को पकड़ते हैं, सवाल यह है कि कैसे?
+0

भले ही विधि में कोई लॉकिंग या थ्रेडिंग न हो, जैसा कि आप कहते हैं, कृपया इसे अभी भी पोस्ट करें। यह एक ढांचा विधि को कॉल कर रहा है जो कवर के तहत लॉक का उपयोग करता है। आम तौर पर, यदि स्टैक निशान आपको बताते हैं कि एक विशेष विधि अवरुद्ध हो रही है, तो यह मानना ​​सुरक्षित है कि वह विधि * वास्तव में अवरुद्ध है *। (स्थैतिक कन्स्ट्रक्टर अपने नाम ('.cctor') के साथ एक अलग विधि है।) इसके अलावा, संपूर्ण स्टैक ट्रेस केवल अंतिम फ्रेम की तुलना में अधिक उपयोगी है। –

+0

मैंने कोड –

+1

कोड के साथ एक ग्रिस्ट को जोड़ा - मेरा बुरा - मुझे इस तथ्य को याद आया कि कॉल विधि निकाय से नहीं है बल्कि 'प्रेस्ट्यूब मोडरफ्रेम' है, जो सीएलआर आंतरिक लॉक को शामिल करने का सुझाव देता है। अधिक प्रासंगिक जानकारी जो आप जोड़ सकते हैं: अप्रबंधित कॉल स्टैक (जैसा कि 'केबी' के साथ लौटाया गया है), और विशेष रूप से ताला के मालिक के धागे (ऊपर दिए गए उदाहरण में 44)। यदि कॉल स्टैक * लॉक के मालिक के धागे में से एक है (मुझे यकीन नहीं है कि "44" यहां थ्रेड आईडी है), तो आपको यह पता लगाना होगा कि वह थ्रेड वास्तव में क्या इंतजार कर रहा है (कौन सा हैंडल पास हो गया है 'ZwWaitForSingleObject')। स्टैक ट्रेस वहां मदद करनी चाहिए। –

उत्तर

1

मेरा अनुमान है कि क्या होता है।

अद्यतन: मुझे लगता है कि यह असेंबली रिसोलव घटना के साथ एक पुनरावर्तन समस्या है। टिप्पणियों के आधार पर, एक स्टैक ओवरफ़्लो नहीं हुआ था, लेकिन फिर भी एक रिकर्सन समस्या हो सकती है, इसलिए उत्तर अभी भी लागू होता है।

एक संकेत है कि यह बग संसाधनों तक पहुंचने के आदेश पर निर्भर करता है। सबसे अधिक संभावना यह तब होती है जब पहली बात आपके द्वारा वर्णित स्थिर वर्गों में से एक तक पहुंच होती है।

जब आप पहली बार संसाधन तक पहुंचते हैं, तो एक असेंबली रीसोल्व घटना कई बार आग लगती है। बाद के संसाधन अनुरोधों का नतीजा विधानसभाओं में नहीं होता है। यह निम्न कोड द्वारा प्रदर्शन किया जा सकता है:

AppDomain.CurrentDomain.AssemblyResolve += (sender, eventArgs) => 
{ 
    Console.WriteLine("Resolve {0}", eventArgs.Name); 
    return null; 
}; 
Console.WriteLine(Resource1.String1); 
Console.WriteLine(Resource1.String1); 

परिणाम:

Resolve ConsoleApplication1.resources, Version=1.0.0.0, Culture=ru-RU, PublicKeyToken=null 
Resolve ConsoleApplication1.resources, Version=1.0.0.0, Culture=ru-RU, PublicKeyToken=null 
Resolve ConsoleApplication1.resources, Version=1.0.0.0, Culture=ru, PublicKeyToken=null 
Resolve ConsoleApplication1.resources, Version=1.0.0.0, Culture=ru, PublicKeyToken=null 
Value from resource 
Value from resource 

लॉगर संसाधनों तक पहुँच रहा है, और इस से निर्देशित होता है:

000000c898899ff0 00007ff885b92014 System.Resources.ResourceManager.GetString(System.String, System.Globalization.CultureInfo) 
000000c89889a0a0 00007ff89914aa62 NewRelic.Agent.Core.Config.ConfigurationLoader.InitializeFromXml(System.String, System.String) 
000000c89889a140 00007ff89914a838 NewRelic.Agent.Core.Config.ConfigurationLoader.Initialize(System.String) 
000000c89889a1a0 00007ff899143be9 NewRelic.Agent.Core.Config.ConfigurationLoader.Initialize() 
000000c89889a210 00007ff899123a27 NewRelic.Agent.Core.Agent+AgentSingleton.CreateInstance() 
000000c89889a280 00007ff8991239c2 NewRelic.Agent.Core.Singleton`1[[System.__Canon, mscorlib]]..ctor(System.__Canon) 
000000c89889a2c0 00007ff89912388b NewRelic.Agent.Core.Agent..cctor() 
000000c89889a700 00007ff8e4b2a7f3 [GCFrame: 000000c89889a700] 
000000c89889ce88 00007ff8e4b2a7f3 [PrestubMethodFrame: 000000c89889ce88] NewRelic.Agent.Core.Agent.get_Instance() 
000000c89889cef0 00007ff89912358c NewRelic.Agent.Core.AgentShim.GetTracer(System.String, UInt32, System.String, System.String, System.Type, System.String, System.String, System.String, System.Object, System.Object[]) 

मेरा निष्कर्ष है कि यहाँ लकड़हारा सकता है असेंबली के बिना सफलतापूर्वक चलाएं किसी भी घटना को पहली बार बाध्य करें, और कभी भी विधानसभा रिजोलव घटना का कारण नहीं बनता है, अगर यह पहली बार इस तरह से चलता है।

यदि आप किसी असेंबली रीसोल्व से पहली बार संसाधन तक पहुंचते हैं, तो एक रिकर्सिव कॉल होता है, जो स्टैक ओवरफ्लो एक्सेप्शन की ओर जाता है।यह मॉडल के लिए आसान है:

AppDomain.CurrentDomain.AssemblyResolve += (sender, eventArgs) => 
{ 
    Console.WriteLine("Resolve {0}", eventArgs.Name); 
    Console.WriteLine(Resource1.String1); 
    return null; 
}; 

Console.WriteLine(Resource1.String1); 

और वहाँ लॉगर के लिए एक कॉल है:

catch 
{ 
    context.RunnerLog.Error(string.Format(CultureInfo.InvariantCulture, "Failed to load assembly {0}.", args.Name)); 

    result = null; 
} 

एक अंतर है, वहाँ हो सकता है अगर लकड़हारा AssemblyResolve घटना से पहले प्रारंभ किया गया था ही था, या वहाँ एक और शर्त यह थी कि लॉगर ने एक असफल असेंबली रिजोलव घटना को आग लगाने का कारण नहीं बनाया।

जब आपने एक स्थैतिक वर्ग के लिए कॉल शुरू किया था और विधानसभा में अपवाद है, और आप इसे पकड़ने और लॉगिंग करने के लिए माना जाता है, तो लॉगर को कॉल करने से संसाधन तक पहुंच होती है, और यह एक और असेंबली का हल करता है और यह रिकर्सन ढेर ओवरफ्लो की ओर जाता है।

जबकि पहले अनुरोध में स्थैतिक वर्ग कन्स्ट्रक्टर पर लॉक होता है, यदि वह ऑपरेशन स्टैक ओवरफ्लो एक्सेप्शन से पहले लंबे समय तक हो रहा था, तो अन्य अनुरोध अवरुद्ध हो गए हैं, लेकिन इससे कोई फर्क नहीं पड़ता, क्योंकि वे TypeInitializationException के साथ विफल हो जाएंगे। उत्तरार्द्ध कभी नहीं होगा, क्योंकि डोमेन StackOverflowException के बाद भी अनलोड करना शुरू कर देगा।

तथ्य यह है कि यह कुछ शब्दकोश दिखा रहा है शीर्ष पर विधि ढूंढें इससे कोई फर्क नहीं पड़ता - यह शायद आखिरी बूंद है जो स्टैक ओवरफ्लो में योगदान देता है।

एक बात मैं विधानसभाResolve घटना हैंडलर के अंदर एक और प्रकार के लॉगर का उपयोग करने की सिफारिश करता हूं।

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

हालांकि, मुझे नहीं लगता कि संदिग्ध स्टैक ओवरफ्लो का कारण स्थिर रचनाकारों के साथ करना है।

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

+0

अब हम असेंबली समाधान विधि को हटाने के लिए उपलब्ध हैं। यदि आप सही हैं, तो यह त्रुटि अब और प्रकट नहीं होनी चाहिए, है ना? –

+0

मैं समस्या को पुन: उत्पन्न करने के साथ शुरू करूंगा। –

+0

मेरे प्रस्तावित डेमो में एक छोटा ऐप बनाएं, अपने लॉगर के साथ संसाधन एक्सेस को प्रतिस्थापित करें (आपके प्रोड ऐप के समान सेटअप के साथ)। संसाधन एक्सेस को बदलें (Console.WriteLine (Resource1.String1);) RunnerLog.Error() के साथ; कॉल, और आपको स्टैक ओवरफ्लो का अनुभव करना चाहिए। तो आप निश्चित हो जाएगा। यदि नहीं, तो सुनिश्चित करें कि लॉगर सेटअप एक असेंबलीResolve ईवेंट उठाता है, क्योंकि यह संदिग्ध कारण है। –

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