2009-11-11 14 views
66

मैंने एक कक्षा और कई यूनिट परीक्षण लिखे हैं, लेकिन मैंने इसे थ्रेड सुरक्षित नहीं बनाया है। अब, मैं कक्षा धागा को सुरक्षित बनाना चाहता हूं, लेकिन इसे साबित करने और टीडीडी का उपयोग करने के लिए, मैं रिफैक्टरिंग शुरू करने से पहले कुछ असफल यूनिट परीक्षण लिखना चाहता हूं।थ्रेड सुरक्षित-नस्ल के लिए यूनिट परीक्षण?

ऐसा करने का कोई अच्छा तरीका है?

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

+0

मैंने आपके द्वारा पहले वर्णित तरीके से यूनिट परीक्षण किए हैं, लेकिन हमेशा महसूस किया जाता है कि परिणाम में यादृच्छिकता का एक तत्व है, इसलिए मैं यहां ब्याज के साथ उत्तरों का पालन करूंगा: o) –

+3

डुप्लिकेट: http: // stackoverflow.com/questions/1715822/unit-test-for-thread-safe-ness – JeffH

+12

@ जेफ हाँ, आपको यह अधिकार मिला। Pffffff, एक एसओ पुलिस होने की कोशिश छोड़ दिया। –

उत्तर

21

दो उत्पादों है कि तुम वहाँ में मदद कर सकते हैं:

अपने कोड में गतिरोध (इकाई परीक्षण के माध्यम से) के लिए दोनों की जांच और मैं दौड़ के लिए शतरंज की जाँच करता लगता है साथ ही स्थितियां भी

दोनों टूल्स का उपयोग करना आसान है - आप एक साधारण इकाई परीक्षण लिखते हैं और अपना कोड कई बार चलाते हैं और जांचते हैं कि आपके कोड में डेडलॉक्स/दौड़ की स्थिति संभव है या नहीं।

संपादित करें: गूगल के लिए एक उपकरण है जो रनटाइम (परीक्षण के दौरान नहीं) में दौड़ शर्त यह है कि thread-race-test कहा जाता है के लिए जाँच करता जारी किया है।
यह सभी दौड़ स्थितियों को नहीं ढूंढ पाएगा क्योंकि यह केवल मौजूदा रन का विश्लेषण करता है, न कि ऊपर दिए गए टूल जैसे सभी संभावित परिदृश्यों पर, लेकिन यह होने पर दौड़ की स्थिति खोजने में आपकी सहायता हो सकती है।

अद्यतन: टाइपेमॉक साइट के पास रेसर का कोई लिंक नहीं था, और यह पिछले 4 वर्षों में अपडेट नहीं किया गया था। मुझे लगता है कि परियोजना बंद थी।

+0

मुझे लगता है कि एमएस शतरंज लिंक टूट गया है। इसे आजमाएं: http://research.microsoft.com/en-us/projects/chess/default.aspx – jpbochi

+1

टाइपपेक रेसर भी टूटा हुआ प्रतीत होता है। इसे आज़माएं: http://site.typemock.com/typemock-racer – jpbochi

+0

एमएस शतरंज लिंक अब तक काम करता प्रतीत होता है। Typemock रेसर लिंक फिक्स्ड। –

10

समस्या यह है कि बहु-थ्रेडिंग मुद्दों जैसे कि दौड़ की स्थिति, उनकी प्रकृति द्वारा गैर-निर्धारक हैं। वे हार्डवेयर व्यवहार पर निर्भर कर सकते हैं जिसे आप संभवतः अनुकरण या ट्रिगर नहीं कर सकते हैं।

इसका मतलब है, भले ही आप एकाधिक धागे के साथ परीक्षण करते हैं, यदि आपके कोड में कोई दोष है तो वे लगातार विफल नहीं होंगे।

+0

-1 "... हार्डवेयर व्यवहार ... संभवतः अनुकरण नहीं कर सकता ..."। ऐसा लगता है कि कम से कम सरल मामलों के लिए, इंटरलीविंग एक्सेस संयोजनों की संख्या सीमित है और इसकी गणना की जा सकती है (यद्यपि "किसी भी तरह") और फिर कोड को कुछ संयोजन थ्रेड शेड्यूलर के माध्यम से प्रत्येक संयोजन का उपयोग करने के लिए मजबूर होना पड़ता है। कुछ बयान कहते हैं कि शतरंज 100% कवरेज है। यदि यह सच है तो हार्डवेयर एक कारक नहीं होना चाहिए। – crokusek

1

स्प्रिंगफ्रेमवर्क परीक्षण मॉड्यूल (या अन्य एक्सटेंशन) के साथ testNG या Junit समवर्ती परीक्षण के लिए मूलभूत समर्थन है।

यह लिंक आपको

http://www.cs.rice.edu/~javaplt/papers/pppj2009.pdf

1

आप चिंता का प्रत्येक संगामिति परिदृश्य के लिए एक टेस्ट केस का निर्माण करना होगा रुचि हो सकती है; इस दावे

विशिष्ट परीक्षण मामलों के बिना की संभावना को बढ़ाने के लिए छोरों में धीमी समकक्ष (या mocks) के साथ कुशल संचालन की जगह और कई परीक्षण चलाने की आवश्यकता हो सकती, यह विशिष्ट परीक्षण

कुछ संभावित रूप से उपयोगी संदर्भ प्रस्ताव करने के लिए मुश्किल है सामग्री:

3

मैंने देखा है कि लोग मानक प्रस्तावों के साथ परीक्षण करने का प्रयास करते हैं जैसा कि आप स्वयं प्रस्तावित करते हैं। परीक्षण धीमे हैं, और हमारी कंपनी के साथ संघर्ष करने वाली समेकन समस्याओं में से एक को पहचानने में अब तक असफल रहा है।

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

किसी भी तरह से मैं किसी के लिए कुछ ऐसा करने के लिए प्यार करता हूं जो विपरीत को इंगित कर सकता है, इसलिए मैं इस सवाल को बारीकी से देखता हूं।

2

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

// This interface is optional, but is probably a good idea. 
public interface ImportantFacade 
{ 
    void ImportantMethodThatMustBeThreadSafe(); 
} 

// This class provides the thread safe-ness (see usage below). 
public class ImportantTransaction : IDisposable 
{ 
    public ImportantFacade Facade { get; private set; } 
    private readonly Lock _lock; 

    public ImportantTransaction(ImportantFacade facade, Lock aLock) 
    { 
     Facade = facade; 
     _lock = aLock; 
     _lock.Lock(); 
    } 

    public void Dispose() 
    { 
     _lock.Unlock(); 
    } 
} 

// I create a lock interface to be able to fake locks in my tests. 
public interface Lock 
{ 
    void Lock(); 
    void Unlock(); 
} 

// This is the implementation I want in my production code for Lock. 
public class LockWithMutex : Lock 
{ 
    private Mutex _mutex; 

    public LockWithMutex() 
    { 
     _mutex = new Mutex(false); 
    } 

    public void Lock() 
    { 
     _mutex.WaitOne(); 
    } 

    public void Unlock() 
    { 
     _mutex.ReleaseMutex(); 
    } 
} 

// This is the transaction provider. This one should replace all your 
// instances of ImportantImplementation in your code today. 
public class ImportantProvider<T> where T:Lock,new() 
{ 
    private ImportantFacade _facade; 
    private Lock _lock; 

    public ImportantProvider(ImportantFacade facade) 
    { 
     _facade = facade; 
     _lock = new T(); 
    } 

    public ImportantTransaction CreateTransaction() 
    { 
     return new ImportantTransaction(_facade, _lock); 
    } 
} 

// This is your old class. 
internal class ImportantImplementation : ImportantFacade 
{ 
    public void ImportantMethodThatMustBeThreadSafe() 
    { 
     // Do things 
    } 
} 

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

// Make sure this is the only way to create ImportantImplementation. 
// Consider making ImportantImplementation an internal class of the provider. 
ImportantProvider<LockWithMutex> provider = 
    new ImportantProvider<LockWithMutex>(new ImportantImplementation()); 

// Create a transaction that will be disposed when no longer used. 
using (ImportantTransaction transaction = provider.CreateTransaction()) 
{ 
    // Access your object thread safe. 
    transaction.Facade.ImportantMethodThatMustBeThreadSafe(); 
} 

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

सुनिश्चित करें कि लेनदेन का निपटारा सही ढंग से किया जा सकता है और यदि आप अपने आवेदन में अजीब व्यवहार नहीं देख सकते हैं। आप इस तरह की चीजों को देखने के लिए माइक्रोसॉफ्ट शतरंज (जैसा कि किसी अन्य उत्तरदाता में सुझाया गया है) के रूप में उपकरण का उपयोग कर सकते हैं।या फिर आप अपने प्रदाता मुखौटा लागू है और यह इस तरह इसे लागू कर सकते हैं:

public void ImportantMethodThatMustBeThreadSafe() 
    { 
     using (ImportantTransaction transaction = CreateTransaction()) 
     { 
      transaction.Facade.ImportantMethodThatMustBeThreadSafe(); 
     } 
    } 

हालांकि इस कार्यान्वयन मुझे आशा है कि आप परीक्षण यह पता लगाने इन कक्षाओं में सत्यापित करने के लिए कर सकते हैं के रूप में की जरूरत है।

5

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

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

मुझे इस उपकरण की सटीक आंतरिक कार्यप्रणाली नहीं पता है, और यह इन टैग स्ट्रिंग्स को कोड पर वापस कैसे चिह्नित करता है जिसे आप डेडलॉक को ठीक करने के लिए बदल रहे हैं, लेकिन वहां आपके पास है ... मैं वास्तव में वास्तव में आगे देख रहा हूं इस उपकरण (और Pex) को वीएस आईडीई का हिस्सा बनने के लिए।

1

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

// from linqpad 

void Main() 
{ 
    var duration = TimeSpan.FromSeconds(5); 
    var td = new ThreadDangerous(); 

    // no problems using single thread (run this for as long as you want) 
    foreach (var x in Until(duration)) 
     td.DoSomething(); 

    // thread dangerous - it won't take long at all for this to blow up 
    try 
    {   
     Parallel.ForEach(WhileTrue(), x => 
      td.DoSomething()); 

     throw new Exception("A ThreadDangerException should have been thrown"); 
    } 
    catch(AggregateException aex) 
    { 
     // make sure that the exception thrown was related 
     // to thread danger 
     foreach (var ex in aex.Flatten().InnerExceptions) 
     { 
      if (!(ex is ThreadDangerException)) 
       throw; 
     } 
    } 

    // no problems using multiple threads (run this for as long as you want) 
    var ts = new ThreadSafe(); 
    Parallel.ForEach(Until(duration), x => 
     ts.DoSomething());  

} 

class ThreadDangerous 
{ 
    private Guid test; 
    private readonly Guid ctrl; 

    public void DoSomething() 
    {   
     test = Guid.NewGuid(); 
     test = ctrl;   

     if (test != ctrl) 
      throw new ThreadDangerException(); 
    } 
} 

class ThreadSafe 
{ 
    private Guid test; 
    private readonly Guid ctrl; 
    private readonly object _lock = new Object(); 

    public void DoSomething() 
    { 
     lock(_lock) 
     { 
      test = Guid.NewGuid(); 
      test = ctrl;   

      if (test != ctrl) 
       throw new ThreadDangerException(); 
     } 
    } 
} 

class ThreadDangerException : Exception 
{ 
    public ThreadDangerException() : base("Not thread safe") { } 
} 

IEnumerable<ulong> Until(TimeSpan duration) 
{ 
    var until = DateTime.Now.Add(duration); 
    ulong i = 0; 
    while (DateTime.Now < until) 
    { 
     yield return i++; 
    } 
} 

IEnumerable<ulong> WhileTrue() 
{ 
    ulong i = 0; 
    while (true) 
    { 
     yield return i++; 
    } 
} 

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

मैं मानता हूं कि यह इसके बारे में जाने का एक प्राचीन तरीका हो सकता है और जटिल परिदृश्यों में मदद नहीं कर सकता है।

0

मेरा दृष्टिकोण यहां है। यह परीक्षण डेडलॉक्स से चिंतित नहीं है, यह स्थिरता से चिंतित है। मैं एक तुल्यकालन ब्लॉक के साथ एक विधि का परीक्षण कर रहा हूँ, कोड है कि कुछ इस तरह दिखता साथ:

synchronized(this) { 
    int size = myList.size(); 
    // do something that needs "size" to be correct, 
    // but which will change the size at the end. 
    ... 
} 

यह एक परिदृश्य है कि मज़बूती से एक धागा संघर्ष का उत्पादन करेगा निर्माण करने के लिए कठिन है, लेकिन यहाँ मैं क्या किया है।

सबसे पहले, मेरे यूनिट परीक्षण ने 50 धागे बनाए, उन्हें एक ही समय में लॉन्च किया, और उन सभी ने मेरी विधि को कॉल किया। मैं उन सभी को एक ही समय में शुरू करने के लिए के लिए उलटी गिनती कुंडी का उपयोग करें:

CountDownLatch latch = new CountDownLatch(1); 
for (int i=0; i<50; ++i) { 
    Runnable runner = new Runnable() { 
    latch.await(); // actually, surround this with try/catch InterruptedException 
    testMethod(); 
    } 
    new Thread(runner, "Test Thread " +ii).start(); // I always name my threads. 
} 
// all threads are now waiting on the latch. 
latch.countDown(); // release the latch 
// all threads are now running the test method at the same time. 

यह या एक संघर्ष का उत्पादन नहीं हो सकता है। मेरा testMethod() एक संघर्ष होने पर अपवाद फेंकने में सक्षम होना चाहिए। लेकिन हम अभी तक यह सुनिश्चित नहीं कर सकते कि इससे संघर्ष उत्पन्न होगा। तो हम नहीं जानते कि परीक्षण मान्य है या नहीं। तो यहां चाल है: अपने सिंक्रनाइज़ किए गए कीवर्ड (ओं) पर टिप्पणी करें और परीक्षण चलाएं। यदि यह एक संघर्ष पैदा करता है, तो परीक्षण विफल हो जाएगा। यदि यह सिंक्रनाइज़ किए गए कीवर्ड के बिना विफल रहता है, तो आपका परीक्षण मान्य है।

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

एक बार जब मैं इस कोड को बाहरी लूप के अंदर रखता हूं, तो मैं बाहरी लूप के 20 वें पुनरावृत्ति पर असफलता को देखने में सक्षम था।अब मुझे विश्वास था कि परीक्षण मान्य था, और मैंने वास्तविक परीक्षण चलाने के लिए सिंक्रनाइज़ किए गए कीवर्ड को पुनर्स्थापित किया। (यह काम किया।)

आप खोज सकते हैं कि परीक्षण एक मशीन पर मान्य है और दूसरे पर नहीं। यदि परीक्षण एक मशीन पर मान्य है और आपके तरीके परीक्षण पास करते हैं, तो यह संभवतः सभी मशीनों पर थ्रेड-सुरक्षित है। लेकिन आपको उस मशीन पर वैधता की जांच करनी चाहिए जो आपके रात के यूनिट परीक्षण चलाती है।

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