रिट्री लॉजिक लिखने का सबसे साफ तरीका?


455

कभी-कभी मुझे हार मानने से पहले कई बार ऑपरेशन टालना पड़ता है। मेरा कोड इस प्रकार है:

int retries = 3;
while(true) {
  try {
    DoSomething();
    break; // success!
  } catch {
    if(--retries == 0) throw;
    else Thread.Sleep(1000);
  }
}

मैं इसे एक सामान्य पुनः प्राप्ति समारोह में फिर से लिखना चाहूंगा जैसे:

TryThreeTimes(DoSomething);

क्या यह C # में संभव है? TryThreeTimes()विधि के लिए कोड क्या होगा ?


1
एक साधारण चक्र पर्याप्त नहीं है? क्यों नहीं कई बार तर्क करना और उस पर अमल करना?
रेस्टुटा

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

12
मेरे मामले में, मेरे DoSomething () फ़ाइलों को हटाने, या नेटवर्क पोर्ट को हिट करने का प्रयास करने जैसी दूरस्थ मशीनों पर सामान कर रहे हैं। दोनों ही मामलों में, DoSomething सफल होगा और दूरस्थता की वजह से, मेरे द्वारा सुनने पर कोई घटना नहीं होती है। तो हाँ, इसकी बदबूदार। सुझाव का स्वागत करते हैं।
22

13
@PavelMinaev खराब समग्र डिजाइन पर रिट्रीट संकेत का उपयोग क्यों करेगा? यदि आप बहुत सारे कोड लिखते हैं जो एकीकरण बिंदुओं को जोड़ता है तो रिट्रीस का उपयोग करना निश्चित रूप से एक पैटर्न है जिसे आपको गंभीरता से उपयोग करने पर विचार करना चाहिए।
बट्टेदेव

जवाबों:


569

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

public static class Retry
{
    public static void Do(
        Action action,
        TimeSpan retryInterval,
        int maxAttemptCount = 3)
    {
        Do<object>(() =>
        {
            action();
            return null;
        }, retryInterval, maxAttemptCount);
    }

    public static T Do<T>(
        Func<T> action,
        TimeSpan retryInterval,
        int maxAttemptCount = 3)
    {
        var exceptions = new List<Exception>();

        for (int attempted = 0; attempted < maxAttemptCount; attempted++)
        {
            try
            {
                if (attempted > 0)
                {
                    Thread.Sleep(retryInterval);
                }
                return action();
            }
            catch (Exception ex)
            {
                exceptions.Add(ex);
            }
        }
        throw new AggregateException(exceptions);
    }
}

अब आप इस उपयोगिता विधि का उपयोग पुनः तर्क करने के लिए कर सकते हैं:

Retry.Do(() => SomeFunctionThatCanFail(), TimeSpan.FromSeconds(1));

या:

Retry.Do(SomeFunctionThatCanFail, TimeSpan.FromSeconds(1));

या:

int result = Retry.Do(SomeFunctionWhichReturnsInt, TimeSpan.FromSeconds(1), 4);

या आप एक asyncअधिभार भी बना सकते हैं ।


7
+1, विशेष रूप से चेतावनी और त्रुटि-जाँच के लिए। यदि यह एक सामान्य पैरामीटर (जहां T: अपवाद) के रूप में पकड़ने के लिए अपवाद के प्रकार में पारित हो जाता है, तो मैं अधिक आरामदायक होगा।
ट्रूविल

1
यह मेरा इरादा था कि "रिट्रीट" वास्तव में रिट्रीट का मतलब था। लेकिन "कोशिश" करने के लिए इसे बदलना बहुत मुश्किल नहीं है। जब तक नाम सार्थक रखा जाता है। कोड को बेहतर बनाने के अन्य अवसर हैं, जैसे कि नकारात्मक रिट्रीट या नकारात्मक टाइमआउट के लिए जाँच - उदाहरण के लिए। मैंने इन उदाहरणों को सरल बनाए रखने के लिए इनको छोड़ दिया ... लेकिन फिर, व्यवहार में ये शायद कार्यान्वयन के लिए अच्छी वृद्धि होगी।
LBushkin

40
हम अपने DB एक्सेस के लिए एक समान पैटर्न का उपयोग उच्च मात्रा में बिज़टाल ऐप में करते हैं, लेकिन दो सुधारों के साथ: हमारे पास अपवादों के लिए ब्लैकलिस्ट हैं जिन्हें वापस नहीं लिया जाना चाहिए और हम पहले अपवाद को संग्रहीत करते हैं जो तब होता है और फेंकते हैं जब रीट अंततः विफल हो जाती है। कारण यह है कि अक्सर दूसरे और निम्नलिखित अपवाद पहले वाले से अलग होते हैं। उस मामले में आप प्रारंभिक समस्या को छिपाते हैं जब केवल अंतिम अपवाद को फिर से उखाड़ फेंकते हैं।
टीटोनी

2
@Dexters हम आंतरिक अपवाद के रूप में मूल अपवाद के साथ एक नया अपवाद फेंकते हैं। मूल स्टैक ट्रेस आंतरिक अपवादों से विशेषता के रूप में उपलब्ध है।
टीटोनी

7
आप इसे संभालने के लिए पोली जैसे ओपन सोर्स लाइब्रेरी का उपयोग करने का भी प्रयास कर सकते हैं । रिट्रीट के बीच प्रतीक्षा करने के लिए बहुत अधिक लचीलापन है और यह कई अन्य लोगों द्वारा मान्य किया गया है जिन्होंने परियोजना का उपयोग किया है। उदाहरण: Policy.Handle<DivideByZeroException>().WaitAndRetry(new[] { TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(3) });
टोड माइनरशैगन

220

आपको पोली की कोशिश करनी चाहिए । यह मेरे द्वारा लिखी गई एक .NET लाइब्रेरी है जो डेवलपर्स को क्षणिक अपवाद से निपटने की नीतियों जैसे कि रिट्री, रिट्री फॉरएवर, प्रतीक्षा और रिट्री या सर्किट ब्रेकर को धाराप्रवाह तरीके से व्यक्त करने की अनुमति देती है।

उदाहरण

Policy
    .Handle<SqlException>(ex => ex.Number == 1205)
    .Or<ArgumentException>(ex => ex.ParamName == "example")
    .WaitAndRetry(3, retryAttempt => TimeSpan.FromSeconds(3))
    .Execute(() => DoSomething());

1
वास्तव में OnRetry प्रतिनिधि क्या है? मुझे लगता है कि जब अपवाद होता है तो हमें प्रदर्शन करने की आवश्यकता होती है। इसलिए जब अपवाद होता है तो OnRetry प्रतिनिधि कॉल करेगा और बाद में Execute प्रतिनिधि। ऐसा है क्या?
user6395764

60

यह संभवतः एक बुरा विचार है। सबसे पहले, यह मैक्सिम का प्रतीक है "पागलपन की परिभाषा दो बार एक ही काम कर रही है और हर बार अलग परिणाम की उम्मीद कर रही है"। दूसरा, यह कोडिंग पैटर्न अपने आप में अच्छी तरह से रचना नहीं करता है। उदाहरण के लिए:

मान लीजिए कि आपकी नेटवर्क हार्डवेयर परत विफलता के बीच एक पैकेट को तीन बार विफल होने पर इंतजार करती है, कहते हैं, विफलताओं के बीच एक दूसरा।

अब मान लीजिए कि सॉफ्टवेयर की परत पैकेट विफलता पर तीन बार विफलता के बारे में एक अधिसूचना का समाधान करती है।

अब मान लें कि अधिसूचना परत एक अधिसूचना वितरण विफलता पर अधिसूचना को तीन बार पुन: सक्रिय करती है।

अब मान लीजिए कि त्रुटि रिपोर्टिंग परत एक अधिसूचना विफलता पर तीन बार अधिसूचना परत को पुनः सक्रिय करती है।

और अब मान लीजिए कि वेब सर्वर त्रुटि विफलता पर तीन बार रिपोर्टिंग को पुनः सक्रिय करता है।

और अब मान लीजिए कि वेब क्लाइंट को सर्वर से एक त्रुटि मिलने पर तीन बार अनुरोध का समाधान करता है।

अब मान लें कि अधिसूचना को रूट करने के लिए नेटवर्क स्विच पर लाइन को अनप्लग किया गया है। वेब क्लाइंट के उपयोगकर्ता को आखिरकार अपना त्रुटि संदेश कब मिलता है? मैं इसे लगभग बारह मिनट बाद बनाता हूं।

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

आमतौर पर एक त्रुटि स्थिति के साथ करने के लिए सही बात यह तुरंत रिपोर्ट है और उपयोगकर्ता को यह तय करने दें कि क्या करना है। यदि उपयोगकर्ता स्वचालित रीट्रीज़ की नीति बनाना चाहता है, तो उसे सॉफ़्टवेयर एब्स्ट्रैक्शन में उपयुक्त स्तर पर उस नीति को बनाने दें।


19
+1। रेमंड ने यहां एक वास्तविक जीवन का उदाहरण साझा किया है, blogs.msdn.com/oldnewthing/archive/2005/11/07/489807.aspx
SolutionYogi

210
-1 यह सलाह स्वचालित बैच प्रोसेसिंग सिस्टमों द्वारा सामना किए गए क्षणिक नेटवर्क विफलताओं के लिए बेकार है।
नोहट

15
यकीन नहीं होता कि अगर यह "डोंट डू इट" इसके बाद "डू इट" कर रहा है। इस सवाल को पूछने वाले ज्यादातर लोग सॉफ्टवेयर एब्स्ट्रक्शन में काम करने वाले लोग हैं।
जिम एल

44
जब आपके पास लंबे समय तक चलने वाली बैच की नौकरियां होती हैं जो नेटवर्क संसाधनों, जैसे वेब सेवाओं का उपयोग करती हैं, तो आप नेटवर्क को 100% विश्वसनीय होने की उम्मीद नहीं कर सकते हैं। कभी-कभी टाइमआउट, सॉकेट डिस्कनेक्ट होने जा रहे हैं, संभवतः यहां तक ​​कि सहज राउटिंग ग्लिट्स या सर्वर आउटेज भी होते हैं जब आप इसका उपयोग कर रहे होते हैं। एक विकल्प को विफल करना है, लेकिन इसका मतलब बाद में एक लंबी नौकरी को फिर से शुरू करना हो सकता है। एक अन्य विकल्प यह है कि उपयुक्त समय के साथ कुछ समय के लिए फिर से प्रयास करें कि क्या यह एक अस्थायी समस्या है, तो असफल रहें। मैं रचना के बारे में सहमत हूं, जिसके बारे में आपको जानकारी होनी चाहिए .. लेकिन यह कभी-कभी सबसे अच्छा विकल्प होता है।
एरिक Funkenbusch

18
मुझे लगता है कि आपके उत्तर की शुरुआत में आपने जो उद्धरण इस्तेमाल किया है वह दिलचस्प है। "विभिन्न परिणामों की अपेक्षा करना" केवल पागलपन है यदि पूर्व अनुभव नियमित रूप से आपको समान परिणाम देता है। जबकि सॉफ्टवेयर स्थिरता के वादे पर बनाया गया है, निश्चित रूप से ऐसी परिस्थितियां हैं जहां हमें अपने नियंत्रण से बाहर अविश्वसनीय बलों के साथ बातचीत करने की आवश्यकता होती है।
माइकल रिचर्डसन

49
public void TryThreeTimes(Action action)
{
    var tries = 3;
    while (true) {
        try {
            action();
            break; // success!
        } catch {
            if (--tries == 0)
                throw;
            Thread.Sleep(1000);
        }
    }
}

तब आप फोन करेंगे:

TryThreeTimes(DoSomething);

... या वैकल्पिक रूप से ...

TryThreeTimes(() => DoSomethingElse(withLocalVariable));

अधिक लचीला विकल्प:

public void DoWithRetry(Action action, TimeSpan sleepPeriod, int tryCount = 3)
{
    if (tryCount <= 0)
        throw new ArgumentOutOfRangeException(nameof(tryCount));

    while (true) {
        try {
            action();
            break; // success!
        } catch {
            if (--tryCount == 0)
                throw;
            Thread.Sleep(sleepPeriod);
        }
   }
}

के रूप में इस्तेमाल किया जा करने के लिए:

DoWithRetry(DoSomething, TimeSpan.FromSeconds(2), tryCount: 10);

Async / प्रतीक्षा के समर्थन के साथ एक और अधिक आधुनिक संस्करण:

public async Task DoWithRetryAsync(Func<Task> action, TimeSpan sleepPeriod, int tryCount = 3)
{
    if (tryCount <= 0)
        throw new ArgumentOutOfRangeException(nameof(tryCount));

    while (true) {
        try {
            await action();
            return; // success!
        } catch {
            if (--tryCount == 0)
                throw;
            await Task.Delay(sleepPeriod);
        }
   }
}

के रूप में इस्तेमाल किया जा करने के लिए:

await DoWithRetryAsync(DoSomethingAsync, TimeSpan.FromSeconds(2), tryCount: 10);

2
अधिमानतः यदि इसे बदल दें: --retryCount <= 0क्योंकि यह हमेशा के लिए चलेगा यदि आप इसे 0. पर सेट करके रिट्रीव करना चाहते हैं, तो तकनीकी रूप से यह शब्द retryCountवास्तव में अच्छा नाम नहीं है, क्योंकि यदि आप इसे 1 पर सेट करते हैं तो यह पुनः प्रयास नहीं करेगा। या तो नाम बदलें यह tryCountया पीछे डाल दिया।
स्टेफान्वड्स

2
@ शैल मैं सहमत हूँ। हालाँकि ओपी (और अन्य सभी उत्तर) उपयोग कर रहे हैं Thread.Sleep। वैकल्पिक रूप से टाइमर का उपयोग करना है, या अधिक संभावना है कि आजकल asyncरिट्री के साथ उपयोग किया जाए Task.Delay
ड्रू नोक

2
मैंने एक async संस्करण जोड़ा है।
ड्रू नोक

केवल तोड़ अगर कार्रवाई returns true? Func<bool>
किकेनेट

32

क्षणिक दोष हैंडलिंग आवेदन ब्लॉक सहित पुन: प्रयास करें रणनीतियों में से एक विस्तृत संग्रह प्रदान करता है:

  • इंक्रीमेंटल
  • निश्चित अंतराल
  • घातीय बैक-ऑफ

इसमें क्लाउड-आधारित सेवाओं के लिए त्रुटि का पता लगाने की रणनीतियों का एक संग्रह भी शामिल है।

अधिक जानकारी के लिए डेवलपर गाइड के इस अध्याय को देखें ।

NuGet (' पुखराज ' की खोज ) के माध्यम से उपलब्ध है ।


1
दिलचस्प। क्या आप Windows Azure के बाहर इसका उपयोग कर सकते हैं, एक Winforms ऐप में कह सकते हैं?
मैथ्यू लॉक

6
पूर्ण रूप से। कोर रिट्री मैकेनिज्म का उपयोग करें और अपनी पहचान की रणनीतियां प्रदान करें। हमने जानबूझकर उन लोगों को डिक्लेयर किया। यहां कोर नगेट पैकेज खोजें: nuget.org/packages/TransientFaultHandling.Core
Grigori Melnik

2
इसके अलावा, परियोजना अब अपाचे 2.0 के तहत है और सामुदायिक योगदान को स्वीकार कर रही है। aka.ms/entlibopen
ग्रिगोरी मेलनिक

1
@Alex। इसके टुकड़े इसे मंच में बना रहे हैं।
ग्रिगोरियो मेलनिक

2
यह अब पदावनत हो गया है, और अंतिम बार मैंने इसका इस्तेमाल किया जिसमें कुछ कीड़े थे जो मुझे पता था कि नहीं थे, और कभी भी निश्चित नहीं होंगे: github.com/MicrosoftArchive/…
ओहद श्नाइडर

15

कार्यों के लिए अनुमति देना और संदेशों को पुन: प्रयास करना

public static T RetryMethod<T>(Func<T> method, int numRetries, int retryTimeout, Action onFailureAction)
{
 Guard.IsNotNull(method, "method");            
 T retval = default(T);
 do
 {
   try
   {
     retval = method();
     return retval;
   }
   catch
   {
     onFailureAction();
      if (numRetries <= 0) throw; // improved to avoid silent failure
      Thread.Sleep(retryTimeout);
   }
} while (numRetries-- > 0);
  return retval;
}

RetryMethod को फिर से बेचना सच है, याmax retries?
कीकनेट

14

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

RetryForExcpetionType(DoSomething, typeof(TimeoutException), 5, 1000);

public static void RetryForExcpetionType(Action action, Type retryOnExceptionType, int numRetries, int retryTimeout)
{
    if (action == null)
        throw new ArgumentNullException("action");
    if (retryOnExceptionType == null)
        throw new ArgumentNullException("retryOnExceptionType");
    while (true)
    {
        try
        {
            action();
            return;
        }
        catch(Exception e)
        {
            if (--numRetries <= 0 || !retryOnExceptionType.IsAssignableFrom(e.GetType()))
                throw;

            if (retryTimeout > 0)
                System.Threading.Thread.Sleep(retryTimeout);
        }
    }
}

आप यह भी नोट कर सकते हैं कि अन्य सभी उदाहरणों में रिट्रीस == 0 के परीक्षण के साथ एक समान मुद्दा है और नकारात्मक मूल्य दिए जाने पर या तो अनंतता को प्राप्त करने या अपवादों को उठाने में विफल रहते हैं। इसके अलावा स्लीप (-1000) ऊपर के कैच ब्लॉक में विफल हो जाएगी। इस बात पर निर्भर करता है कि आप किस तरह से 'मूर्खतापूर्ण' लोगों से उम्मीद करते हैं लेकिन रक्षात्मक प्रोग्रामिंग कभी नुकसान नहीं पहुंचाती है।


9
+1, लेकिन रिट्रीटफ़ॉरसेप्शन <T> (...) क्यों नहीं करते जहां टी: अपवाद, फिर पकड़ (टी ई)? बस यह कोशिश की और यह पूरी तरह से काम करता है।
ट्रूविल

या तो यहाँ या जब से मुझे कुछ भी करने की आवश्यकता नहीं है टाइप के साथ मैं प्रदान करता हूँ मुझे लगता है कि एक सादे पुराने पैरामीटर चाल कर देगा।
csharptest.net

@TrueWill जाहिरा तौर पर पकड़ (टी पूर्व) इस पोस्ट के अनुसार कुछ कीड़े है stackoverflow.com/questions/1577760/…
csharptest.net

3
अद्यतन: वास्तव में एक बेहतर कार्यान्वयन जो मैं उपयोग कर रहा हूं वह एक पूर्व निर्धारित <अपवाद> प्रतिनिधि को लेता है जो एक रिट्री उपयुक्त होने पर सही रिटर्न देता है। यह आपको मूल त्रुटि कोड या अपवाद के अन्य गुणों का उपयोग करने की अनुमति देता है ताकि यह निर्धारित किया जा सके कि रिट्री लागू है। उदाहरण के लिए HTTP 503 कोड।
csharptest.net

1
"इसके अलावा स्लीप (-1000) ऊपर के कैच ब्लॉक्स में विफल हो जाएगा ..." टाइमस्पैन का उपयोग करें और आपको यह समस्या नहीं होगी। प्लस टाइमस्पैन बहुत अधिक लचीला और आत्म वर्णनात्मक है। "Int retryTimeout" के आपके हस्ताक्षर से मुझे कैसे पता चलेगा कि अगर retryTimeout MS, सेकंड, मिनट, वर्ष है ?? ;-)
बट्टेदेव

13

मैं पुनरावृत्ति और विस्तार विधियों का प्रशंसक हूं, इसलिए यहां मेरे दो सेंट हैं:

public static void InvokeWithRetries(this Action @this, ushort numberOfRetries)
{
    try
    {
        @this();
    }
    catch
    {
        if (numberOfRetries == 0)
            throw;

        InvokeWithRetries(@this, --numberOfRetries);
    }
}

7

पिछले काम पर बिल्डिंग, मैंने तीन तरीकों से रिट्री लॉजिक को बढ़ाने के बारे में सोचा:

  1. निर्दिष्ट करना कि किस प्रकार के अपवाद को पकड़ना / पुनः प्रयास करना है यह प्राथमिक उत्साह है क्योंकि किसी भी अपवाद के लिए पुन: प्रयास करना केवल गलत है।
  2. थोड़ी कोशिश में बेहतर प्रदर्शन हासिल करते हुए, कोशिश / पकड़ में आखिरी कोशिश नहीं करना
  3. इसे Actionविस्तार की विधि बनाना

    static class ActionExtensions
    {
      public static void InvokeAndRetryOnException<T> (this Action action, int retries, TimeSpan retryDelay) where T : Exception
      {
        if (action == null)
          throw new ArgumentNullException("action");
    
        while( retries-- > 0 )
        {
          try
          {
            action( );
            return;
          }
          catch (T)
          {
            Thread.Sleep( retryDelay );
          }
        }
    
        action( );
      }
    }

विधि को फिर से लागू किया जा सकता है (जैसे अनाम तरीकों का इस्तेमाल किया जा सकता है, निश्चित रूप से):

new Action( AMethodThatMightThrowIntermittentException )
  .InvokeAndRetryOnException<IntermittentException>( 2, TimeSpan.FromSeconds( 1 ) );

1
यह उत्कृष्ट है। लेकिन व्यक्तिगत रूप से मैं इसे 'रिट्रीटटाइम' नहीं कहूंगा क्योंकि यह वास्तव में टाइमआउट नहीं है। 'रिट्रीडेले', शायद?
होल्फ

7

इसे C # 6.0 के साथ सरल रखें

public async Task<T> Retry<T>(Func<T> action, TimeSpan retryInterval, int retryCount)
{
    try
    {
        return action();
    }
    catch when (retryCount != 0)
    {
        await Task.Delay(retryInterval);
        return await Retry(action, retryInterval, --retryCount);
    }
}

2
मैं जिज्ञासु हूं, क्या यह एक समान प्रतीक्षा पद्धति को वापस करने के कारण उच्च रिट्री काउंट और अंतराल के साथ थ्रेड्स की एक पागल राशि है?
शिकार २४

6

पोली का उपयोग करें

https://github.com/App-vNext/Polly-Samples

यहाँ मैं पोली के साथ एक रिट्री-जेनेरिक उपयोग कर रहा हूँ

public T Retry<T>(Func<T> action, int retryCount = 0)
{
    PolicyResult<T> policyResult = Policy
     .Handle<Exception>()
     .Retry(retryCount)
     .ExecuteAndCapture<T>(action);

    if (policyResult.Outcome == OutcomeType.Failure)
    {
        throw policyResult.FinalException;
    }

    return policyResult.Result;
}

इसे ऐसे उपयोग करें

var result = Retry(() => MyFunction()), 3);

5

नवीनतम अंदाज में कार्यान्वित किया गया LBushkin का उत्तर:

    public static async Task Do(Func<Task> task, TimeSpan retryInterval, int maxAttemptCount = 3)
    {
        var exceptions = new List<Exception>();
        for (int attempted = 0; attempted < maxAttemptCount; attempted++)
        {
            try
            {
                if (attempted > 0)
                {
                    await Task.Delay(retryInterval);
                }

                await task();
                return;
            }
            catch (Exception ex)
            {
                exceptions.Add(ex);
            }
        }
        throw new AggregateException(exceptions);
    }

    public static async Task<T> Do<T>(Func<Task<T>> task, TimeSpan retryInterval, int maxAttemptCount = 3)
    {
        var exceptions = new List<Exception>();
        for (int attempted = 0; attempted < maxAttemptCount; attempted++)
        {
            try
            {
                if (attempted > 0)
                {
                    await Task.Delay(retryInterval);
                }
                return await task();
            }
            catch (Exception ex)
            {
                exceptions.Add(ex);
            }
        }
        throw new AggregateException(exceptions);
    }  

और इसका उपयोग करने के लिए:

await Retry.Do([TaskFunction], retryInterval, retryAttempts);

जबकि फ़ंक्शन [TaskFunction]या तो हो सकता है Task<T>या बस Task


1
धन्यवाद, फैबियन! यह शीर्ष करने के लिए सभी तरह से उत्कीर्ण किया जाना चाहिए!
जेम्सहॉक्स

1
@MarkLauter संक्षिप्त जवाब हाँ है। ;-)
फेबियन बिगलर

4

मैं इसे लागू करूंगा:

public static bool Retry(int maxRetries, Func<bool, bool> method)
{
    while (maxRetries > 0)
    {
        if (method(maxRetries == 1))
        {
            return true;
        }
        maxRetries--;
    }
    return false;        
}

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

क्यों यह एक है Func<bool, bool>और सिर्फ एक नहीं Func<bool>? ताकि अगर मैं चाहता हूं कि एक विधि विफलता पर एक अपवाद को फेंकने में सक्षम हो, तो मेरे पास यह सूचित करने का एक तरीका है कि यह आखिरी कोशिश है।

इसलिए मैं इसे कोड के साथ उपयोग कर सकता हूं जैसे:

Retry(5, delegate(bool lastIteration)
   {
       // do stuff
       if (!succeeded && lastIteration)
       {
          throw new InvalidOperationException(...)
       }
       return succeeded;
   });

या

if (!Retry(5, delegate(bool lastIteration)
   {
       // do stuff
       return succeeded;
   }))
{
   Console.WriteLine("Well, that didn't work.");
}

अगर एक पैरामीटर है कि विधि का उपयोग करें अजीब साबित होता नहीं है गुजर, यह की एक अधिभार लागू करने के लिए तुच्छ है Retryसिर्फ एक लेता है कि Func<bool>रूप में अच्छी तरह।


1
अपवाद से बचने के लिए +1। हालांकि मैं एक शून्य रिट्री (...) करूंगा और कुछ फेंक दूंगा? बूलियन रिटर्न और / या रिटर्न कोड की अक्सर अनदेखी की जाती है।
csharptest.net

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

मुझे संदर्भ नहीं मिल रहा है लेकिन मेरा मानना ​​है कि .NET एक अपवाद को "एक विधि के रूप में परिभाषित नहीं करता है जो उसने कहा था कि वह ऐसा करेगा"। 1 उद्देश्य अपवाद का उपयोग करने के लिए समस्या को इंगित करने के लिए Win32 पैटर्न के बजाय एक समस्या को इंगित करने के लिए होता है जो फ़ंक्शन के सफल होने या न होने पर रिटर्न वैल्यू की जांच करने की आवश्यकता होती है।
रात 8

लेकिन अपवाद केवल "एक समस्या का संकेत नहीं है।" इनमें नैदानिक ​​जानकारी का एक समूह भी शामिल है जो संकलन के लिए समय और स्मृति खर्च करता है। इसमें स्पष्ट रूप से ऐसी स्थितियां हैं जो कम से कम थोड़ा सा मायने नहीं रखती हैं। लेकिन बहुत कुछ है जहाँ यह करता है। .NET नियंत्रण प्रवाह के अपवादों का उपयोग नहीं करता है (तुलना करें, कहते हैं, अजगर StopIterationअपवाद के उपयोग के साथ), और इसका एक कारण है।
रॉबर्ट रोसनी

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

2

एक्सपोनेंशियल बैकऑफ़ केवल एक्स संख्या की कोशिश करने की तुलना में एक अच्छी रीट्री रणनीति है। इसे लागू करने के लिए आप पोली जैसी लाइब्रेरी का उपयोग कर सकते हैं।


2

उन लोगों के लिए जो किसी भी अपवाद पर पुन: प्रयास करने या अपवाद प्रकार को स्पष्ट रूप से सेट करने के लिए दोनों विकल्प चाहते हैं, इसका उपयोग करें:

public class RetryManager 
{
    public void Do(Action action, 
                    TimeSpan interval, 
                    int retries = 3)
    {
        Try<object, Exception>(() => {
            action();
            return null;
        }, interval, retries);
    }

    public T Do<T>(Func<T> action, 
                    TimeSpan interval, 
                    int retries = 3)
    {
        return Try<T, Exception>(
              action
            , interval
            , retries);
    }

    public T Do<E, T>(Func<T> action, 
                       TimeSpan interval, 
                       int retries = 3) where E : Exception
    {
        return Try<T, E>(
              action
            , interval
            , retries);
    }

    public void Do<E>(Action action, 
                       TimeSpan interval, 
                       int retries = 3) where E : Exception
    {
        Try<object, E>(() => {
            action();
            return null;
        }, interval, retries);
    }

    private T Try<T, E>(Func<T> action, 
                       TimeSpan interval, 
                       int retries = 3) where E : Exception
    {
        var exceptions = new List<E>();

        for (int retry = 0; retry < retries; retry++)
        {
            try
            {
                if (retry > 0)
                    Thread.Sleep(interval);
                return action();
            }
            catch (E ex)
            {
                exceptions.Add(ex);
            }
        }

        throw new AggregateException(exceptions);
    }
}

2

मुझे एक विधि की आवश्यकता थी जो रद्द करने का समर्थन करती है, जबकि मैं उस पर था, मैंने मध्यवर्ती विफलताओं को वापस करने के लिए समर्थन जोड़ा।

public static class ThreadUtils
{
    public static RetryResult Retry(
        Action target,
        CancellationToken cancellationToken,
        int timeout = 5000,
        int retries = 0)
    {
        CheckRetryParameters(timeout, retries)
        var failures = new List<Exception>();
        while(!cancellationToken.IsCancellationRequested)
        {
            try
            {
                target();
                return new RetryResult(failures);
            }
            catch (Exception ex)
            {
                failures.Add(ex);
            }

            if (retries > 0)
            {
                retries--;
                if (retries == 0)
                {
                    throw new AggregateException(
                     "Retry limit reached, see InnerExceptions for details.",
                     failures);
                }
            }

            if (cancellationToken.WaitHandle.WaitOne(timeout))
            {
                break;
            }
        }

        failures.Add(new OperationCancelledException(
            "The Retry Operation was cancelled."));
        throw new AggregateException("Retry was cancelled.", failures);
    }

    private static void CheckRetryParameters(int timeout, int retries)
    {
        if (timeout < 1)
        {
            throw new ArgumentOutOfRangeException(...
        }

        if (retries < 0)
        {
            throw new ArgumentOutOfRangeException(...

        }
    }

    public class RetryResult : IEnumerable<Exception>
    {
        private readonly IEnumerable<Exception> failureExceptions;
        private readonly int failureCount;

         protected internal RetryResult(
             ICollection<Exception> failureExceptions)
         {
             this.failureExceptions = failureExceptions;
             this.failureCount = failureExceptions.Count;
         }
    }

    public int FailureCount
    {
        get { return this.failureCount; }
    }

    public IEnumerator<Exception> GetEnumerator()
    {
        return this.failureExceptions.GetEnumerator();
    }

    System.Collections.IEnumerator 
        System.Collections.IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }
}

आप Retryफ़ंक्शन का उपयोग इस तरह कर सकते हैं , 10 सेकंड की देरी के साथ 3 बार पुन: प्रयास करें लेकिन बिना रद्द किए।

try
{
    var result = ThreadUtils.Retry(
        SomeAction, 
        CancellationToken.None,
        10000,
        3);

    // it worked
    result.FailureCount // but failed this many times first.
}
catch (AggregationException ex)
{
   // oops, 3 retries wasn't enough.
}

या, जब तक रद्द नहीं किया जाता है, तब तक हर पांच सेकंड में पुनः प्रयास करें।

try
{
    var result = ThreadUtils.Retry(
        SomeAction, 
        someTokenSource.Token);

    // it worked
    result.FailureCount // but failed this many times first.
}
catch (AggregationException ex)
{
   // operation was cancelled before success.
}

जैसा कि आप अनुमान लगा सकते हैं, मेरे स्रोत कोड में मैंने Retryअलग-अलग डेलगेट प्रकारों का उपयोग करने की इच्छा के लिए फ़ंक्शन को अधिभारित किया है।


2

यह विधि कुछ अपवाद प्रकारों पर पुन: प्रयास करने की अनुमति देती है (दूसरों को तुरंत फेंक देती है)।

public static void DoRetry(
    List<Type> retryOnExceptionTypes,
    Action actionToTry,
    int retryCount = 5,
    int msWaitBeforeEachRety = 300)
{
    for (var i = 0; i < retryCount; ++i)
    {
        try
        {
            actionToTry();
            break;
        }
        catch (Exception ex)
        {
            // Retries exceeded
            // Throws on last iteration of loop
            if (i == retryCount - 1) throw;

            // Is type retryable?
            var exceptionType = ex.GetType();
            if (!retryOnExceptionTypes.Contains(exceptionType))
            {
                throw;
            }

            // Wait before retry
            Thread.Sleep(msWaitBeforeEachRety);
        }
    }
}
public static void DoRetry(
    Type retryOnExceptionType,
    Action actionToTry,
    int retryCount = 5,
    int msWaitBeforeEachRety = 300)
        => DoRetry(new List<Type> {retryOnExceptionType}, actionToTry, retryCount, msWaitBeforeEachRety);

उदाहरण उपयोग:

DoRetry(typeof(IOException), () => {
    using (var fs = new FileStream(requestedFilePath, FileMode.Create, FileAccess.Write))
    {
        fs.Write(entryBytes, 0, entryBytes.Length);
    }
});

1

6 साल बाद अपडेट करें: अब मैं समझता हूं कि नीचे का दृष्टिकोण काफी खराब है। एक रिट्री लॉजिक बनाने के लिए हमें पोली जैसी लाइब्रेरी का उपयोग करने पर विचार करना चाहिए।


asyncरीट्री पद्धति का मेरा कार्यान्वयन:

public static async Task<T> DoAsync<T>(Func<dynamic> action, TimeSpan retryInterval, int retryCount = 3)
    {
        var exceptions = new List<Exception>();

        for (int retry = 0; retry < retryCount; retry++)
        {
            try
            {
                return await action().ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                exceptions.Add(ex);
            }

            await Task.Delay(retryInterval).ConfigureAwait(false);
        }
        throw new AggregateException(exceptions);
    }

मुख्य बिंदु: मैंने इस्तेमाल किया .ConfigureAwait(false);और Func<dynamic>इसके बजायFunc<T>


यह प्रश्न का उत्तर प्रदान नहीं करता है। कृपया पृष्ठ के शीर्ष पर "प्रश्न पूछें" बटन का उपयोग करके एक नए प्रश्न के रूप में अपने उत्तर को पोस्ट करने पर विचार करें, फिर समुदाय के साथ जो सीखा है उसे साझा करने के लिए प्रश्न का अपना उत्तर पोस्ट करें।
एलिक्सेनाइड

कोडरेव्यू.स्टैकएक्सचेंज.com/q/55983/54000 की तुलना में C # 5.0 के साथ बहुत सरल है लेकिन शायद CansellactionToken को इंजेक्ट किया जाना चाहिए।
सर्ग

इस कार्यान्वयन में कोई समस्या है। अंतिम रिट्रीट के बाद, देने से ठीक पहले, Task.Delayबिना किसी कारण के बुलाया जाता है।
HappyNomad 16

@HappyNomad यह एक 6 साल पुराना जवाब है और अब मैं समझता हूं कि एक बहुत बुरा दृष्टिकोण एक रिट्री लॉजिक बनाने के लिए :)) अधिसूचना के लिए धन्यवाद। मैं उस विचार के अनुसार अपने उत्तर को अपडेट करूंगा।
सिहान उइगुन

0

या कैसे यह एक छोटा सा करने के बारे में ....

int retries = 3;
while (retries > 0)
{
  if (DoSomething())
  {
    retries = 0;
  }
  else
  {
    retries--;
  }
}

मेरा मानना ​​है कि अपवादों को फेंकने को आम तौर पर एक तंत्र के रूप में देखा जाना चाहिए जब तक कि आपकी सीमा उन्हें सीमाओं के बीच से गुजरती नहीं है (जैसे कि एक पुस्तकालय का उपयोग अन्य लोग कर सकते हैं)। यदि यह सफल था और नहीं तो सिर्फ DoSomething()कमान वापस क्यों नहीं ?truefalse

संपादित करें: और यह एक फ़ंक्शन के अंदर समझाया जा सकता है जैसे कि दूसरों ने भी सुझाव दिया है। केवल समस्या यह है कि यदि आप DoSomething()फ़ंक्शन स्वयं नहीं लिख रहे हैं


7
"मेरा मानना ​​है कि अपवादों को फेंकने से आमतौर पर एक तंत्र के रूप में बचा जाना चाहिए जब तक कि आपकी सीमाओं के बीच उन्हें पारित नहीं किया जाता है" - मैं पूरी तरह से असहमत हूं। आपको कैसे पता चलेगा कि कॉलर ने आपका झूठा (या बुरा, अशक्त) रिटर्न चेक किया है? कोड विफल क्यों हुआ? झूठा आपको और कुछ नहीं बताता। क्या होगा अगर कॉल करने वाले को स्टैक अप विफलता पारित करना है? Msdn.microsoft.com/en-us/library/ms229014.aspx पढ़ें - ये पुस्तकालयों के लिए हैं, लेकिन वे आंतरिक कोड के लिए सिर्फ उतना ही अर्थ रखते हैं। और एक टीम पर, अन्य लोगों को आपके कोड को कॉल करने की संभावना है।
ट्रूविल

0

मुझे फिर से प्रयास करने के लिए अपनी विधि के लिए कुछ पैरामीटर पारित करने की आवश्यकता थी, और एक परिणाम मूल्य है; इसलिए मुझे एक अभिव्यक्ति की आवश्यकता है .. मैं इस वर्ग का निर्माण करता हूं जो काम करता है (यह LBushkin के लिए प्रेरित है) आप इसे इस तरह से उपयोग कर सकते हैं:

static void Main(string[] args)
{
    // one shot
    var res = Retry<string>.Do(() => retryThis("try"), 4, TimeSpan.FromSeconds(2), fix);

    // delayed execute
    var retry = new Retry<string>(() => retryThis("try"), 4, TimeSpan.FromSeconds(2), fix);
    var res2 = retry.Execute();
}

static void fix()
{
    Console.WriteLine("oh, no! Fix and retry!!!");
}

static string retryThis(string tryThis)
{
    Console.WriteLine("Let's try!!!");
    throw new Exception(tryThis);
}

public class Retry<TResult>
{
    Expression<Func<TResult>> _Method;
    int _NumRetries;
    TimeSpan _RetryTimeout;
    Action _OnFailureAction;

    public Retry(Expression<Func<TResult>> method, int numRetries, TimeSpan retryTimeout, Action onFailureAction)
    {
        _Method = method;
        _NumRetries = numRetries;
        _OnFailureAction = onFailureAction;
        _RetryTimeout = retryTimeout;
    }

    public TResult Execute()
    {
        TResult result = default(TResult);
        while (_NumRetries > 0)
        {
            try
            {
                result = _Method.Compile()();
                break;
            }
            catch
            {
                _OnFailureAction();
                _NumRetries--;
                if (_NumRetries <= 0) throw; // improved to avoid silent failure
                Thread.Sleep(_RetryTimeout);
            }
        }
        return result;
    }

    public static TResult Do(Expression<Func<TResult>> method, int numRetries, TimeSpan retryTimeout, Action onFailureAction)
    {
        var retry = new Retry<TResult>(method, numRetries, retryTimeout, onFailureAction);
        return retry.Execute();
    }
}

ps। LBushkin का समाधान एक और पुनः प्रयास करता है = D


0

मैं निम्नलिखित कोड को स्वीकृत उत्तर में जोड़ दूंगा

public static class Retry<TException> where TException : Exception //ability to pass the exception type
    {
        //same code as the accepted answer ....

        public static T Do<T>(Func<T> action, TimeSpan retryInterval, int retryCount = 3)
        {
            var exceptions = new List<Exception>();

            for (int retry = 0; retry < retryCount; retry++)
            {
                try
                {
                    return action();
                }
                catch (TException ex) //Usage of the exception type
                {
                    exceptions.Add(ex);
                    Thread.Sleep(retryInterval);
                }
            }

            throw new AggregateException(String.Format("Failed to excecute after {0} attempt(s)", retryCount), exceptions);
        }
    }

मूल रूप से उपरोक्त कोड बना रहा है Retry कक्षा को सामान्य ताकि आप रिट्रीट के लिए उस अपवाद को पास कर सकें जिसे आप पकड़ना चाहते हैं।

अब इसे लगभग उसी तरह से उपयोग करें लेकिन अपवाद प्रकार निर्दिष्ट करें

Retry<EndpointNotFoundException>.Do(() => SomeFunctionThatCanFail(), TimeSpan.FromSeconds(1));

लूप के लिए हमेशा कुछ समय (आपके रिट्रीटकाउंट के आधार पर) निष्पादित किया जाएगा भले ही TRY CATCH लूप में कोड अपवादों के बिना निष्पादित किया गया हो। मैं कोशिश करूंगा कि कोशिश करने वाले पाश में रिट्री वर्जन के बराबर ही रिट्रीकाउंट सेट करूं, इसलिए लूप वाइल के लिए यह खत्म हो जाएगा।
स्क्रू_डब्ल्यू scre

@scre_www मुझे विश्वास है कि आप गलत हैं। यदि actionनहीं फेंकता है तो Doरिटर्न इस प्रकार लूप breakसे दूर होता है for
HappyNomad

किसी भी मामले में, इस कार्यान्वयन के साथ एक समस्या है। अंतिम रिट्रीट के बाद, देने से ठीक पहले, Thread.Sleepबिना किसी कारण के बुलाया जाता है।
HappyNomad

0

मुझे पता है कि यह उत्तर बहुत पुराना है, लेकिन मैं सिर्फ इस पर टिप्पणी करना चाहता था क्योंकि मैं इनका उपयोग करते हुए मुद्दों में चला गया हूं, करो, काउंटरों के साथ जो भी बयान दें।

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


0

सी #, जावा या अन्य भाषाओं में इसे सरल करें:

  internal class ShouldRetryHandler {
    private static int RETRIES_MAX_NUMBER = 3;
    private static int numberTryes;

    public static bool shouldRetry() {
        var statusRetry = false;

        if (numberTryes< RETRIES_MAX_NUMBER) {
            numberTryes++;
            statusRetry = true;
            //log msg -> 'retry number' + numberTryes

        }

        else {
            statusRetry = false;
            //log msg -> 'reached retry number limit' 
        }

        return statusRetry;
    }
}

और इसे अपने कोड में बहुत सरल उपयोग करें:

 void simpleMethod(){
    //some code

    if(ShouldRetryHandler.shouldRetry()){
    //do some repetitive work
     }

    //some code    
    }

या आप इसे पुनरावर्ती विधियों में उपयोग कर सकते हैं:

void recursiveMethod(){
    //some code

    if(ShouldRetryHandler.shouldRetry()){
    recursiveMethod();
     }

    //some code    
    }

0
int retries = 3;
while (true)
{
    try
    {
        //Do Somthing
        break;
    }
    catch (Exception ex)
    {
        if (--retries == 0)
            return Request.BadRequest(ApiUtil.GenerateRequestResponse(false, "3 Times tried it failed do to : " + ex.Message, new JObject()));
        else
            System.Threading.Thread.Sleep(100);
    }

क्या आप के साथ क्या करते हो Request.BadRequest?
दानह

0

रिट्री हेल्पर: एक जेनेरिक जावा कार्यान्वयन जिसमें वापसी योग्य और शून्य दोनों प्रकार के रिट्रीट शामिल हैं।

import java.util.function.Supplier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class RetryHelper {
  private static final Logger log = LoggerFactory.getLogger(RetryHelper.class);
  private int retryWaitInMS;
  private int maxRetries;

  public RetryHelper() {
    this.retryWaitInMS = 300;
    this.maxRetries = 3;
  }

  public RetryHelper(int maxRetry) {
    this.maxRetries = maxRetry;
    this.retryWaitInMS = 300;
  }

  public RetryHelper(int retryWaitInSeconds, int maxRetry) {
    this.retryWaitInMS = retryWaitInSeconds;
    this.maxRetries = maxRetry;
  }

  public <T> T retryAndReturn(Supplier<T> supplier) {
    try {
      return supplier.get();
    } catch (Exception var3) {
      return this.retrySupplier(supplier);
    }
  }

  public void retry(Runnable runnable) {
    try {
      runnable.run();
    } catch (Exception var3) {
      this.retrySupplier(() -> {
        runnable.run();
        return null;
      });
    }

  }

  private <T> T retrySupplier(Supplier<T> supplier) {
    log.error("Failed <TASK>, will be retried " + this.maxRetries + " times.");
    int retryCounter = 0;

    while(retryCounter < this.maxRetries) {
      try {
        return supplier.get();
      } catch (Exception var6) {
        ++retryCounter;
        log.error("<TASK> failed on retry: " + retryCounter + " of " + this.maxRetries + " with error: " + var6.getMessage());
        if (retryCounter >= this.maxRetries) {
          log.error("Max retries exceeded.");
          throw var6;
        }

        try {
          Thread.sleep((long)this.retryWaitInMS);
        } catch (InterruptedException var5) {
          var5.printStackTrace();
        }
      }
    }

    return supplier.get();
  }

  public int getRetryWaitInMS() {
    return this.retryWaitInMS;
  }

  public int getMaxRetries() {
    return this.maxRetries;
  }
}

उपयोग:

    try {
      returnValue = new RetryHelper().retryAndReturn(() -> performSomeTask(args));
      //or no return type:
      new RetryHelper().retry(() -> mytask(args));
    } catch(Exception ex){
      log.error(e.getMessage());
      throw new CustomException();
    }

0

यहाँ एक है async/ awaitसंस्करण कि समुच्चय अपवाद और समर्थन रद्द।

/// <seealso href="https://docs.microsoft.com/en-us/azure/architecture/patterns/retry"/>
protected static async Task<T> DoWithRetry<T>( Func<Task<T>> action, CancellationToken cancelToken, int maxRetries = 3 )
{
    var exceptions = new List<Exception>();

    for ( int retries = 0; !cancelToken.IsCancellationRequested; retries++ )
        try {
            return await action().ConfigureAwait( false );
        } catch ( Exception ex ) {
            exceptions.Add( ex );

            if ( retries < maxRetries )
                await Task.Delay( 500, cancelToken ).ConfigureAwait( false ); //ease up a bit
            else
                throw new AggregateException( "Retry limit reached", exceptions );
        }

    exceptions.Add( new OperationCanceledException( cancelToken ) );
    throw new AggregateException( "Retry loop was canceled", exceptions );
}

-1
public delegate void ThingToTryDeletage();

public static void TryNTimes(ThingToTryDelegate, int N, int sleepTime)
{
   while(true)
   {
      try
      {
        ThingToTryDelegate();
      } catch {

            if( --N == 0) throw;
          else Thread.Sleep(time);          
      }
}

क्योंकि throw;एकमात्र तरीका अनंत लूप को समाप्त कर दिया गया है, यह विधि वास्तव में "प्रयास करें जब तक कि यह एन बार विफल नहीं हो जाता है" और वांछित " Nजब तक यह सफल नहीं हो जाता है तब तक प्रयास करें "। आपको कॉल की आवश्यकता है break;या return;कॉल के बाद ThingToTryDelegate();अन्यथा इसे लगातार कॉल किया जाएगा यदि यह कभी विफल नहीं होता है। इसके अलावा, यह संकलित नहीं होगा क्योंकि पहले पैरामीटर का TryNTimesकोई नाम नहीं है। -1।
BACON

-1

मैंने यहां पोस्ट किए गए उत्तरों के आधार पर एक छोटी कक्षा लिखी है। उम्मीद है कि यह किसी की मदद करेगा: https://github.com/natenho/resiliency

using System;
using System.Threading;

/// <summary>
/// Classe utilitária para suporte a resiliência
/// </summary>
public sealed class Resiliency
{
    /// <summary>
    /// Define o valor padrão de número de tentativas
    /// </summary>
    public static int DefaultRetryCount { get; set; }

    /// <summary>
    /// Define o valor padrão (em segundos) de tempo de espera entre tentativas
    /// </summary>
    public static int DefaultRetryTimeout { get; set; }

    /// <summary>
    /// Inicia a parte estática da resiliência, com os valores padrões
    /// </summary>
    static Resiliency()
    {
        DefaultRetryCount = 3;
        DefaultRetryTimeout = 0;
    }

    /// <summary>
    /// Executa uma <see cref="Action"/> e tenta novamente DefaultRetryCount vezes  quando for disparada qualquer <see cref="Exception"/> 
    /// </summary>
    /// <param name="action">Ação a ser realizada</param>
    /// <remarks>Executa uma vez e realiza outras DefaultRetryCount tentativas em caso de exceção. Não aguarda para realizar novas tentativa.</remarks>
    public static void Try(Action action)
    {
        Try<Exception>(action, DefaultRetryCount, TimeSpan.FromMilliseconds(DefaultRetryTimeout), null);
    }

    /// <summary>
    /// Executa uma <see cref="Action"/> e tenta novamente determinado número de vezes quando for disparada qualquer <see cref="Exception"/> 
    /// </summary>
    /// <param name="action">Ação a ser realizada</param>
    /// <param name="retryCount">Número de novas tentativas a serem realizadas</param>
    /// <param name="retryTimeout">Tempo de espera antes de cada nova tentativa</param>
    public static void Try(Action action, int retryCount, TimeSpan retryTimeout)
    {
        Try<Exception>(action, retryCount, retryTimeout, null);
    }

    /// <summary>
    /// Executa uma <see cref="Action"/> e tenta novamente determinado número de vezes quando for disparada qualquer <see cref="Exception"/> 
    /// </summary>
    /// <param name="action">Ação a ser realizada</param>
    /// <param name="retryCount">Número de novas tentativas a serem realizadas</param>
    /// <param name="retryTimeout">Tempo de espera antes de cada nova tentativa</param>
    /// <param name="tryHandler">Permitindo manipular os critérios para realizar as tentativas</param>
    public static void Try(Action action, int retryCount, TimeSpan retryTimeout, Action<ResiliencyTryHandler<Exception>> tryHandler)
    {
        Try<Exception>(action, retryCount, retryTimeout, tryHandler);
    }

    /// <summary>
    /// Executa uma <see cref="Action"/> e tenta novamente por até DefaultRetryCount vezes quando for disparada qualquer <see cref="Exception"/> 
    /// </summary>
    /// <param name="action">Ação a ser realizada</param>
    /// <param name="tryHandler">Permitindo manipular os critérios para realizar as tentativas</param>
    /// <remarks>Executa uma vez e realiza outras DefaultRetryCount tentativas em caso de exceção. Aguarda DefaultRetryTimeout segundos antes de realizar nova tentativa.</remarks>
    public static void Try(Action action, Action<ResiliencyTryHandler<Exception>> tryHandler)
    {
        Try<Exception>(action, DefaultRetryCount, TimeSpan.FromSeconds(DefaultRetryTimeout), null);
    }

    /// <summary>
    /// Executa uma <see cref="Action"/> e tenta novamente determinado número de vezes quando for disparada qualquer <see cref="TException"/> 
    /// </summary>
    /// <param name="action">Ação a ser realizada</param>
    /// <remarks>Executa uma vez e realiza outras DefaultRetryCount tentativas em caso de exceção. Aguarda DefaultRetryTimeout segundos antes de realizar nova tentativa.</remarks>
    public static void Try<TException>(Action action) where TException : Exception
    {
        Try<TException>(action, DefaultRetryCount, TimeSpan.FromSeconds(DefaultRetryTimeout), null);
    }

    /// <summary>
    /// Executa uma <see cref="Action"/> e tenta novamente determinado número de vezes quando for disparada qualquer <see cref="TException"/> 
    /// </summary>
    /// <param name="action">Ação a ser realizada</param>
    /// <param name="retryCount"></param>
    public static void Try<TException>(Action action, int retryCount) where TException : Exception
    {
        Try<TException>(action, retryCount, TimeSpan.FromSeconds(DefaultRetryTimeout), null);
    }

    /// <summary>
    /// Executa uma <see cref="Action"/> e tenta novamente determinado número de vezes quando for disparada qualquer <see cref="Exception"/> 
    /// </summary>
    /// <param name="action">Ação a ser realizada</param>
    /// <param name="retryCount"></param>
    /// <param name="retryTimeout"></param>
    public static void Try<TException>(Action action, int retryCount, TimeSpan retryTimeout) where TException : Exception
    {
        Try<TException>(action, retryCount, retryTimeout, null);
    }

    /// <summary>
    /// Executa uma <see cref="Action"/> e tenta novamente determinado número de vezes quando for disparada qualquer <see cref="Exception"/> 
    /// </summary>
    /// <param name="action">Ação a ser realizada</param>
    /// <param name="tryHandler">Permitindo manipular os critérios para realizar as tentativas</param>
    /// <remarks>Executa uma vez e realiza outras DefaultRetryCount tentativas em caso de exceção. Aguarda DefaultRetryTimeout segundos antes de realizar nova tentativa.</remarks>
    public static void Try<TException>(Action action, Action<ResiliencyTryHandler<TException>> tryHandler) where TException : Exception
    {
        Try(action, DefaultRetryCount, TimeSpan.FromSeconds(DefaultRetryTimeout), tryHandler);
    }

    /// <summary>
    /// Executa uma <see cref="Action"/> e tenta novamente determinado número de vezes quando for disparada uma <see cref="Exception"/> definida no tipo genérico
    /// </summary>
    /// <param name="action">Ação a ser realizada</param>
    /// <param name="retryCount">Número de novas tentativas a serem realizadas</param>
    /// <param name="retryTimeout">Tempo de espera antes de cada nova tentativa</param>
    /// <param name="tryHandler">Permitindo manipular os critérios para realizar as tentativas</param>
    /// <remarks>Construído a partir de várias ideias no post <seealso cref="http://stackoverflow.com/questions/156DefaultRetryCount191/c-sharp-cleanest-way-to-write-retry-logic"/></remarks>
    public static void Try<TException>(Action action, int retryCount, TimeSpan retryTimeout, Action<ResiliencyTryHandler<TException>> tryHandler) where TException : Exception
    {
        if (action == null)
            throw new ArgumentNullException(nameof(action));

        while (retryCount-- > 0)
        {
            try
            {
                action();
                return;
            }
            catch (TException ex)
            {
                //Executa o manipulador de exception
                if (tryHandler != null)
                {
                    var callback = new ResiliencyTryHandler<TException>(ex, retryCount);
                    tryHandler(callback);
                    //A propriedade que aborta pode ser alterada pelo cliente
                    if (callback.AbortRetry)
                        throw;
                }

                //Aguarda o tempo especificado antes de tentar novamente
                Thread.Sleep(retryTimeout);
            }
        }

        //Na última tentativa, qualquer exception será lançada de volta ao chamador
        action();
    }

}

/// <summary>
/// Permite manipular o evento de cada tentativa da classe de <see cref="Resiliency"/>
/// </summary>
public class ResiliencyTryHandler<TException> where TException : Exception
{
    #region Properties

    /// <summary>
    /// Opção para abortar o ciclo de tentativas
    /// </summary>
    public bool AbortRetry { get; set; }

    /// <summary>
    /// <see cref="Exception"/> a ser tratada
    /// </summary>
    public TException Exception { get; private set; }

    /// <summary>
    /// Identifca o número da tentativa atual
    /// </summary>
    public int CurrentTry { get; private set; }

    #endregion

    #region Constructors

    /// <summary>
    /// Instancia um manipulador de tentativa. É utilizado internamente
    /// por <see cref="Resiliency"/> para permitir que o cliente altere o
    /// comportamento do ciclo de tentativas
    /// </summary>
    public ResiliencyTryHandler(TException exception, int currentTry)
    {
        Exception = exception;
        CurrentTry = currentTry;
    }

    #endregion

}

-1

मैंने स्वीकार किए गए उत्तर का एक async संस्करण लागू किया है जैसे - और यह अच्छी तरह से काम करने लगता है - कोई टिप्पणी?


        public static async Task DoAsync(
            Action action,
            TimeSpan retryInterval,
            int maxAttemptCount = 3)
        {
            DoAsync<object>(() =>
            {
                action();
                return null;
            }, retryInterval, maxAttemptCount);
        }

        public static async Task<T> DoAsync<T>(
            Func<Task<T>> action,
            TimeSpan retryInterval,
            int maxAttemptCount = 3)
        {
            var exceptions = new List<Exception>();

            for (int attempted = 0; attempted < maxAttemptCount; attempted++)
            {
                try
                {
                    if (attempted > 0)
                    {
                        Thread.Sleep(retryInterval);
                    }
                    return await action();
                }
                catch (Exception ex)
                {
                    exceptions.Add(ex);
                }
            }
            throw new AggregateException(exceptions);
        }

और, इसे इस तरह से कॉल करें:

var result = await Retry.DoAsync(() => MyAsyncMethod(), TimeSpan.FromSeconds(5), 4);

Thread.Sleep? एक धागा को अवरुद्ध करना अतुल्यकालिक के लाभों को नकारता है। इसके अलावा, मुझे पूरा यकीन है कि Task DoAsync()संस्करण को एक प्रकार का तर्क स्वीकार करना चाहिए Func<Task>
थियोडोर ज़ूलियास
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.