2011-02-21 25 views
9

का कारण बनता है वर्तमान में हम यूनिट परीक्षण के दौरान कुछ मुद्दों का सामना कर रहे हैं। हमारी कक्षा राइनो मोक्स का उपयोग करके मॉक ऑब्जेक्ट्स पर कुछ फ़ंक्शन कॉल मल्टीथ्रेड कर रही है।मल्टीथ्रेड कोड बनाता है राइनो मोक्स डेडलॉक

public class Bar 
{ 
    private readonly List<IFoo> _fooList; 

    public Bar(List<IFoo> fooList) 
    { 
     _fooList = fooList; 
    } 

    public void Start() 
    { 
     var allTasks = new List<Task>(); 
     foreach (var foo in _fooList) 
      allTasks.Add(Task.Factory.StartNew(() => foo.DoSomething())); 

     Task.WaitAll(allTasks.ToArray()); 
    } 
} 

इंटरफ़ेस IFoo के रूप में परिभाषित किया गया है:: यहाँ एक उदाहरण न्यूनतम करने के लिए कम है

public interface IFoo 
{ 
    void DoSomething(); 
    event EventHandler myEvent; 
} 

गतिरोध पुन: पेश करने के लिए, हमारे unittest निम्नलिखित है: 1. कुछ IFoo Mocks 2 पैदा करते हैं। DoEomething() कहा जाता है जब myEvent उठाओ।

[TestMethod] 
    public void Foo_RaiseBar() 
    { 
     var fooList = GenerateFooList(50); 

     var target = new Bar(fooList); 
     target.Start(); 
    } 

    private List<IFoo> GenerateFooList(int max) 
    { 
     var mocks = new MockRepository(); 
     var fooList = new List<IFoo>(); 

     for (int i = 0; i < max; i++) 
      fooList.Add(GenerateFoo(mocks)); 

     mocks.ReplayAll(); 
     return fooList; 
    } 

    private IFoo GenerateFoo(MockRepository mocks) 
    { 
     var foo = mocks.StrictMock<IFoo>(); 

     foo.myEvent += null; 
     var eventRaiser = LastCall.On(foo).IgnoreArguments().GetEventRaiser(); 

     foo.DoSomething(); 
     LastCall.On(foo).WhenCalled(i => eventRaiser.Raise(foo, EventArgs.Empty)); 

     return foo; 
    } 

अधिक फू उत्पन्न होते हैं, अधिकतर डेडलॉक होता है। यदि परीक्षण अवरुद्ध नहीं होगा, तो इसे कई बार चलाएं, और यह होगा। डिबगिंग testrun शो रोक, वह सब कार्य TaskStatus.Running में अब भी कर रहे हैं और वर्तमान कार्यकर्ता धागा

पर टूट रहा है [एक नींद में, प्रतीक्षा करें, या में शामिल होने के]
Rhino.Mocks.DLL! राइनो। Mocks.Impl.RhinoInterceptor.Intercept (Castle.Core.Interceptor.IInvocation मंगलाचरण) + 0x3d

अजीब बात है जो हमें सबसे confuses तथ्य यह है बाइट्स, कि अवरोधन के हस्ताक्षर (...) विधि सिंक्रनाइज़ के रूप में परिभाषित किया गया है - लेकिन कई थ्रेड यहां स्थित हैं। मैंने राइनो मोक्स और मल्टीथ्रेडेड के बारे में कई पोस्टिंग पढ़ी हैं, लेकिन चेतावनी नहीं मिली है (रिकॉर्ड की स्थापना की उम्मीद है) या सीमाएं।

[MethodImpl(MethodImplOptions.Synchronized)] 
    public void Intercept(IInvocation invocation) 

क्या हम अपने मॉकोबजेक्ट्स को स्थापित करने या बहुसंख्यक वातावरण में उनका उपयोग करने पर पूरी तरह से कुछ गलत कर रहे हैं? किसी भी मदद या संकेत का स्वागत है!

+1

मुझे यह पाया गया [http://blog.smithfamily.dk/post/2011/03/26/Thread-safe-version-of-Rhino-Mocks.aspx ](http://blog.smithfamily.dk/ पोस्ट/2011/03/26/थ्रेड-सेफ-वर्जन-राइनो-मोक्स.एएसपीएक्स) मेरी Google यात्रा पर। अफसोस की बात है कि वहां होस्ट किए गए संस्करण में एक बग है, इसलिए मैं नहीं देख सकता कि यह समस्या को हल करता है या नहीं। – jasper

उत्तर

12

यह आपके कोड में दौड़ की स्थिति है और राइनोमोक्स में एक बग नहीं है। समस्या तब होती है जब आप Start() विधि में allTasks कार्य सूची की स्थापना कर रहे हैं:

public void Start() 
{ 
    var allTasks = new List<Task>(); 
    foreach (var foo in _fooList) 
     // the next line has a bug 
     allTasks.Add(Task.Factory.StartNew(() => foo.DoSomething())); 

    Task.WaitAll(allTasks.ToArray()); 
} 

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

इसका मतलब है कि प्रत्येक foo.DoSomething() कभी-कभी कभी-कभी कभी-कभी कभी-कभी कभी-कभी कभी-कभी नहीं बुलाया जाता है। इस कारण से, कुछ कार्य अनिश्चित काल तक अवरुद्ध हो जाएंगे क्योंकि RhinoMocks अलग-अलग धागे से एक ही उदाहरण पर घटनाओं के ओवरलैप्ड राइजिंग को संभाल नहीं सकता है और यह एक डेडलॉक में आता है।

अपने Start विधि में इस लाइन को बदलें:

allTasks.Add(Task.Factory.StartNew(() => foo.DoSomething())); 
इस के साथ

:

allTasks.Add(Task.Factory.StartNew(f => ((IFoo)f).DoSomething(), foo)); 

यह एक क्लासिक बग है कि सूक्ष्म और को नजरअंदाज करना बहुत आसान है है। इसे कभी-कभी "एक संशोधित बंद करने का उपयोग" कहा जाता है।

पुनश्च:

इस पोस्ट पर टिप्पणियों के बाद, मैं इस परीक्षण Moq का उपयोग कर दुबारा लिखा। इस मामले में यह अवरुद्ध नहीं होता है - लेकिन सावधान रहें कि दिए गए उदाहरण पर बनाए गए अपेक्षाएं तब तक संतुष्ट न हों जब तक कि मूल बग को वर्णित नहीं किया जाता है। GenerateFoo() MOQ का उपयोग कर इस तरह दिखता है:

private List<IFoo> GenerateFooList(int max) 
{ 
    var fooList = new List<IFoo>(); 

    for (int i = 0; i < max; i++) 
     fooList.Add(GenerateFoo()); 

    return fooList; 
} 

private IFoo GenerateFoo() 
{ 
    var foo = new Mock<IFoo>(); 
    foo.Setup(f => f.DoSomething()).Raises(f => f.myEvent += null, EventArgs.Empty); 
    return foo.Object; 
} 

यह RhinoMocks की तुलना में अधिक सुंदर है - और एक से अधिक थ्रेड एक साथ एक ही उदाहरण पर घटनाओं को ऊपर उठाने का स्पष्ट रूप से अधिक सहिष्णु। हालांकि मुझे कल्पना नहीं है कि यह एक आम आवश्यकता है - व्यक्तिगत रूप से मुझे अक्सर ऐसे परिदृश्य नहीं मिलते हैं जहां आप किसी ईवेंट में ग्राहकों को थ्रेड-सुरक्षित मान सकते हैं।

+1

+1 - हाँ यह एक फोरच क्लोजर समस्या है (एक अस्थायी चर भी करेगा) - और आपके पास 50 गुना नामक केवल एक फू हो सकता है - लेकिन क्या वह 'उपज' को डेडलॉक करता है? मैं उस परिचित डब्ल्यू/राइनो नहीं हूं (हालांकि नकली एक नकली है :) लेकिन कम से कम कम से कम (नकली कोड), कुछ भी ब्लॉक की तरह दिखता नहीं है। आईएमओ आपको केवल एक फू पर 50 कार्यक्रमों को निकाल दिया जाएगा, लेकिन फिर भी समाप्त होने लगते हैं। – NSGaga

+1

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

+0

जो अधिक समझ में आता है :), अब यह स्पष्ट है - लेकिन यह अभी भी आईएमओ राइनो के डिजाइन की गलती है - क्योंकि इसमें कुछ भी नहीं है जो ताले को औचित्य देता है (हालांकि मैं समझ सकता हूं कि कोई आसानी से कैसे पहुंच सकता है) - यानी यदि आप हैंडक्राफ्ट द मॉक (मैनुअल क्लास) को w/o समस्याओं का काम करना चाहिए - अगर मुझे गलत नहीं है यानी बंदरगाह के साथ बग सिर्फ हुड के नीचे स्थित समस्याओं को ला रहा है ... जबकि यह लॉकिंग को हल कर सकता है (तो यह विशिष्ट क्यू का उत्तर है - लेकिन फिर फिर कोई चाहता था कि राइनो से संबंधित यह साफ़ हो जाए) - आप जल्द ही फंस जाएंगे। मैंने भी डीबग नहीं किया ... – NSGaga

2

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

डीबग -> विंडोज़ -> समांतर ढेर

विजुअल स्टूडियो सभी चल रहे धागे के राज्यों को दिखाते हुए एक अच्छा ग्राफ बनाता है। वहां से आपको आमतौर पर कुछ प्रकार का संकेत मिलता है कि कौन से ताले विवाद में हैं।

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