अतुल्यकालिक रूप से टास्क के लिए प्रतीक्षा करें <T> टाइमआउट के साथ पूरा करने के लिए


387

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

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


3
तुम सही हो। मुझे आश्चर्य है कि यह टाइमआउट के लिए प्रदान नहीं करता है। शायद .NET 5.0 में ... बेशक हम टाइमआउट को कार्य में स्वयं बना सकते हैं, लेकिन यह अच्छा नहीं है, इस तरह की चीजें मुफ्त होनी चाहिए।
अलीओस्ताद

4
हालांकि यह अभी भी आपके द्वारा वर्णित द्वि-स्तरीय टाइमआउट के लिए तर्क की आवश्यकता होगी। .NET 4.5 वास्तव में टाइमआउट-आधारित बनाने के लिए एक सरल विधि प्रदान करता है CancellationTokenSource। कंस्ट्रक्टर को दो अधिभार उपलब्ध हैं, एक पूर्णांक मिलिसकेड विलंब से ले रहा है और एक टाइमस्पैन देरी से ले रहा है।
पितृ पक्ष

यहां पूरा सरल

पूर्ण स्रोत कोड के साथ कोई अंतिम समाधान काम कर रहा है? शायद प्रत्येक थ्रेड में सूचित त्रुटियों के लिए अधिक जटिल नमूना और WaitAll के बाद एक सारांश दिखाता है?
किनिकेत

जवाबों:


563

इस बारे में कैसा है:

int timeout = 1000;
var task = SomeOperationAsync();
if (await Task.WhenAny(task, Task.Delay(timeout)) == task) {
    // task completed within timeout
} else { 
    // timeout logic
}

और यहाँ इस तरह की चीज़ों के बारे में अधिक जानकारी के साथ एक महान ब्लॉग पोस्ट "एक कार्य का प्रारूप तैयार करना" (एमएस समानांतर लाइब्रेरी टीम से)

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

int timeout = 1000;
var task = SomeOperationAsync(cancellationToken);
if (await Task.WhenAny(task, Task.Delay(timeout, cancellationToken)) == task)
{
    // Task completed within timeout.
    // Consider that the task may have faulted or been canceled.
    // We re-await the task so that any exceptions/cancellation is rethrown.
    await task;

}
else
{
    // timeout/cancellation logic
}

86
यह उल्लेख किया जाना चाहिए कि भले ही Task.Delay लंबे समय तक चलने वाले कार्य से पहले पूरा कर सकता है, आपको टाइमआउट परिदृश्य को संभालने की अनुमति देता है, यह लंबे समय तक चलने वाले कार्य को रद्द नहीं करता है; जब कोई भी आपको बताता है कि इसमें से एक कार्य पूरा हो चुका है। आपको एक कैंसलेशनेशन लागू करना होगा और लंबे समय तक चलने वाले कार्य को स्वयं रद्द करना होगा।
जेफ शूमाकर

30
यह भी ध्यान दिया जा सकता है कि यह Task.Delayकार्य एक सिस्टम टाइमर द्वारा समर्थित है जो कि ट्रैकआउट तब तक जारी रहेगा जब तक कि समय समाप्त नहीं SomeOperationAsyncहोता है। इसलिए यदि यह समग्र कोड स्निपेट एक तंग लूप में बहुत अधिक निष्पादित करता है, तो आप टाइमर के लिए सिस्टम संसाधनों का उपभोग कर रहे हैं जब तक कि वे सभी समय समाप्त न हो जाएं। तय करने का तरीका यह होगा CancellationTokenकि आपके पास Task.Delay(timeout, cancellationToken)उस समय पास हो जब आप SomeOperationAsyncटाइमर संसाधन को जारी करने के लिए पूरा करते हैं।
एंड्रयू अरनोट

12
रद्द करने का कोड बहुत अधिक काम कर रहा है। इसे आज़माएँ: int timeout = 1000; var cancellationTokenSource = नया रद्दीकरणTokenSource (टाइमआउट); var cancellationToken = tokenSource.Token; var कार्य = SomeOperationAsync (रद्दीकरण टोकन); कोशिश {प्रतीक्षा कार्य; // सफल समापन के लिए यहां कोड जोड़ें} catch (OperationCancelledException) {// टाइमआउट केस के लिए कोड जोड़ें}
srm

3
@ilans का इंतजार करके Task, कार्य द्वारा संग्रहीत किसी भी अपवाद को उस बिंदु पर फिर से उखाड़ फेंका जाता है। यह आपको पकड़ने का मौका देता है OperationCanceledException(यदि रद्द किया गया है) या कोई अन्य अपवाद (यदि दोषपूर्ण है)।
एंड्रयू अरनोट

3
@TomexOu: सवाल यह है कि के लिए गया था अतुल्यकालिक रूप से एक कार्य के पूरा होने का इंतजार है। Task.Wait(timeout)असिंक्रोनसली प्रतीक्षा के बजाय सिंक्रोनस ब्लॉक करेगा।
एंड्रयू अरनोट

220

यहां एक विस्तार विधि संस्करण है जो मूल उत्तर को एंड्रयू अरनोट द्वारा सुझाए गए सुझाव के अनुसार एक टिप्पणी के जवाब में पूरा करने पर टाइमआउट को रद्द करता है ।

public static async Task<TResult> TimeoutAfter<TResult>(this Task<TResult> task, TimeSpan timeout) {

    using (var timeoutCancellationTokenSource = new CancellationTokenSource()) {

        var completedTask = await Task.WhenAny(task, Task.Delay(timeout, timeoutCancellationTokenSource.Token));
        if (completedTask == task) {
            timeoutCancellationTokenSource.Cancel();
            return await task;  // Very important in order to propagate exceptions
        } else {
            throw new TimeoutException("The operation has timed out.");
        }
    }
}

8
इस आदमी को कुछ वोट दो। सुरुचिपूर्ण समाधान। और अगर आप कहते हैं कि आपके पास रिटर्न प्रकार नहीं है, तो सुनिश्चित करें कि आप बस TResult को हटा दें।
लुकास

6
रद्दीकरण using
टोकेनसेरो

6
@ इटट्रैप एक कार्य का दो बार इंतजार करता है, परिणाम दूसरी प्रतीक्षा पर देता है। यह दो बार निष्पादित नहीं करता है। आप कह सकते हैं कि task.Result दो बार निष्पादित होने पर यह बराबर हो जाता है।
एम। मिम्पेन

7
क्या taskटाइमआउट की स्थिति में मूल कार्य ( ) अभी भी चलता रहेगा?
गुड़

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

49

आप Task.WaitAnyकई कार्यों में से पहले प्रतीक्षा करने के लिए उपयोग कर सकते हैं ।

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


1
मैंने एमवीपी द्वारा इस्तेमाल की गई इस तकनीक को देखा है जिसका मैं वास्तव में सम्मान करता हूं, यह मुझे स्वीकृत उत्तर की तुलना में बहुत साफ लगता है। शायद एक उदाहरण से अधिक वोट प्राप्त करने में मदद मिलेगी! मैं इसे करने के लिए स्वेच्छा से काम करूंगा, सिवाय इसके कि मेरे पास पर्याप्त टास्क अनुभव नहीं है, यह सुनिश्चित करने के लिए उपयोगी होगा :)
ग्राहम मैक

3
एक धागा अवरुद्ध हो जाएगा - लेकिन अगर उर ठीक है तो कोई समस्या नहीं है। मैंने जो समाधान लिया वह नीचे था, क्योंकि कोई भी सूत्र अवरुद्ध नहीं है। मैंने ब्लॉग पोस्ट पढ़ी जो वास्तव में अच्छी थी।
JJschk

@JJschk आप उल्लेख आप समाधान लिया below.... जो है? एसओ के आदेश के आधार पर?
बोझाओ

और क्या होगा यदि मैं धीमी कार्य को रद्द नहीं करना चाहता हूं? मैं इसे संभालना चाहता हूं जब यह खत्म हो जाता है लेकिन वर्तमान पद्धति से वापस आ जाता है ..
अकमल सालिकोव

18

इस तरह के किसी चीज़ के बारे में क्या?

    const int x = 3000;
    const int y = 1000;

    static void Main(string[] args)
    {
        // Your scheduler
        TaskScheduler scheduler = TaskScheduler.Default;

        Task nonblockingTask = new Task(() =>
            {
                CancellationTokenSource source = new CancellationTokenSource();

                Task t1 = new Task(() =>
                    {
                        while (true)
                        {
                            // Do something
                            if (source.IsCancellationRequested)
                                break;
                        }
                    }, source.Token);

                t1.Start(scheduler);

                // Wait for task 1
                bool firstTimeout = t1.Wait(x);

                if (!firstTimeout)
                {
                    // If it hasn't finished at first timeout display message
                    Console.WriteLine("Message to user: the operation hasn't completed yet.");

                    bool secondTimeout = t1.Wait(y);

                    if (!secondTimeout)
                    {
                        source.Cancel();
                        Console.WriteLine("Operation stopped!");
                    }
                }
            });

        nonblockingTask.Start();
        Console.WriteLine("Do whatever you want...");
        Console.ReadLine();
    }

आप किसी अन्य कार्य का उपयोग करके मुख्य थ्रेड को अवरुद्ध किए बिना टास्क.वेट विकल्प का उपयोग कर सकते हैं।


वास्तव में इस उदाहरण में आप t1 के अंदर नहीं बल्कि एक ऊपरी कार्य पर प्रतीक्षा कर रहे हैं। मैं एक अधिक विस्तृत उदाहरण बनाने की कोशिश करूँगा।
रूप में cii

14

यहाँ शीर्ष मतदान के जवाब के आधार पर पूरी तरह से काम किया गया उदाहरण है, जो है:

int timeout = 1000;
var task = SomeOperationAsync();
if (await Task.WhenAny(task, Task.Delay(timeout)) == task) {
    // task completed within timeout
} else { 
    // timeout logic
}

इस उत्तर में कार्यान्वयन का मुख्य लाभ यह है कि जेनरिक जोड़े गए हैं, इसलिए फ़ंक्शन (या कार्य) एक मान लौटा सकता है। इसका मतलब है कि किसी भी मौजूदा फ़ंक्शन को टाइमआउट फ़ंक्शन में लपेटा जा सकता है, जैसे:

इससे पहले:

int x = MyFunc();

उपरांत:

// Throws a TimeoutException if MyFunc takes more than 1 second
int x = TimeoutAfter(MyFunc, TimeSpan.FromSeconds(1));

इस कोड के लिए .NET 4.5 की आवश्यकता है।

using System;
using System.Threading;
using System.Threading.Tasks;

namespace TaskTimeout
{
    public static class Program
    {
        /// <summary>
        ///     Demo of how to wrap any function in a timeout.
        /// </summary>
        private static void Main(string[] args)
        {

            // Version without timeout.
            int a = MyFunc();
            Console.Write("Result: {0}\n", a);
            // Version with timeout.
            int b = TimeoutAfter(() => { return MyFunc(); },TimeSpan.FromSeconds(1));
            Console.Write("Result: {0}\n", b);
            // Version with timeout (short version that uses method groups). 
            int c = TimeoutAfter(MyFunc, TimeSpan.FromSeconds(1));
            Console.Write("Result: {0}\n", c);

            // Version that lets you see what happens when a timeout occurs.
            try
            {               
                int d = TimeoutAfter(
                    () =>
                    {
                        Thread.Sleep(TimeSpan.FromSeconds(123));
                        return 42;
                    },
                    TimeSpan.FromSeconds(1));
                Console.Write("Result: {0}\n", d);
            }
            catch (TimeoutException e)
            {
                Console.Write("Exception: {0}\n", e.Message);
            }

            // Version that works on tasks.
            var task = Task.Run(() =>
            {
                Thread.Sleep(TimeSpan.FromSeconds(1));
                return 42;
            });

            // To use async/await, add "await" and remove "GetAwaiter().GetResult()".
            var result = task.TimeoutAfterAsync(TimeSpan.FromSeconds(2)).
                           GetAwaiter().GetResult();

            Console.Write("Result: {0}\n", result);

            Console.Write("[any key to exit]");
            Console.ReadKey();
        }

        public static int MyFunc()
        {
            return 42;
        }

        public static TResult TimeoutAfter<TResult>(
            this Func<TResult> func, TimeSpan timeout)
        {
            var task = Task.Run(func);
            return TimeoutAfterAsync(task, timeout).GetAwaiter().GetResult();
        }

        private static async Task<TResult> TimeoutAfterAsync<TResult>(
            this Task<TResult> task, TimeSpan timeout)
        {
            var result = await Task.WhenAny(task, Task.Delay(timeout));
            if (result == task)
            {
                // Task completed within timeout.
                return task.GetAwaiter().GetResult();
            }
            else
            {
                // Task timed out.
                throw new TimeoutException();
            }
        }
    }
}

चेतावनियां

इस उत्तर को दिए जाने के बाद, सामान्य ऑपरेशन के दौरान आपके कोड में अपवादों को रखने के लिए इसका अच्छा अभ्यास नहीं है, जब तक कि आपको बिल्कुल ऐसा न करना पड़े:

  • हर बार एक अपवाद को फेंक दिया जाता है, यह एक अत्यंत भारी ऑपरेशन है,
  • अपवाद एक तंग लूप में हैं, तो अपवाद आपके कोड को 100 या अधिक के कारक से धीमा कर सकते हैं।

केवल इस कोड का उपयोग करें यदि आप बिल्कुल उस फ़ंक्शन को बदल नहीं सकते हैं जिसे आप किसी विशिष्ट के बाद इसे बाहर करते हैं TimeSpan

यह जवाब वास्तव में केवल तब लागू होता है जब तृतीय पक्ष पुस्तकालय पुस्तकालयों के साथ काम करता है जिसे आप केवल टाइमआउट पैरामीटर शामिल करने के लिए रिफलेक्टर नहीं कर सकते हैं।

कैसे मजबूत कोड लिखने के लिए

यदि आप मजबूत कोड लिखना चाहते हैं, तो सामान्य नियम यह है:

हर एक ऑपरेशन जो संभावित रूप से अनिश्चित काल के लिए अवरुद्ध हो सकता है, एक समयबाह्य होना चाहिए।

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

यदि कुछ समय बाद एक उचित समय समाप्त हो जाता था, तो आपका ऐप कुछ चरम समय (उदाहरण के लिए 30 सेकंड) के लिए लटका होगा, तब वह या तो एक त्रुटि प्रदर्शित करेगा और अपने मैरी रास्ते पर जारी रहेगा, या फिर से प्रयास करेगा।


11

स्टीफन क्लीयर की उत्कृष्ट AsyncEx लाइब्रेरी का उपयोग करके , आप कर सकते हैं:

TimeSpan timeout = TimeSpan.FromSeconds(10);

using (var cts = new CancellationTokenSource(timeout))
{
    await myTask.WaitAsync(cts.Token);
}

TaskCanceledException समय समाप्त होने की स्थिति में फेंक दिया जाएगा।


10

यह पिछले उत्तरों का थोड़ा बढ़ा हुआ संस्करण है।

  • लॉरेंस के जवाब के अलावा , यह टाइमआउट होने पर मूल कार्य को रद्द कर देता है।
  • करने के लिए इसके अतिरिक्त एस जे बी के जवाब वेरिएंट 2 और 3 , प्रदान कर सकते हैं CancellationTokenमूल कार्य के लिए, और जब समय समाप्त होता है, आप प्राप्त TimeoutExceptionकरने के बजाय OperationCanceledException
async Task<TResult> CancelAfterAsync<TResult>(
    Func<CancellationToken, Task<TResult>> startTask,
    TimeSpan timeout, CancellationToken cancellationToken)
{
    using (var timeoutCancellation = new CancellationTokenSource())
    using (var combinedCancellation = CancellationTokenSource
        .CreateLinkedTokenSource(cancellationToken, timeoutCancellation.Token))
    {
        var originalTask = startTask(combinedCancellation.Token);
        var delayTask = Task.Delay(timeout, timeoutCancellation.Token);
        var completedTask = await Task.WhenAny(originalTask, delayTask);
        // Cancel timeout to stop either task:
        // - Either the original task completed, so we need to cancel the delay task.
        // - Or the timeout expired, so we need to cancel the original task.
        // Canceling will not affect a task, that is already completed.
        timeoutCancellation.Cancel();
        if (completedTask == originalTask)
        {
            // original task completed
            return await originalTask;
        }
        else
        {
            // timeout
            throw new TimeoutException();
        }
    }
}

प्रयोग

InnerCallAsyncपूरा होने में लंबा समय लग सकता है। CallAsyncइसे टाइमआउट के साथ लपेटता है।

async Task<int> CallAsync(CancellationToken cancellationToken)
{
    var timeout = TimeSpan.FromMinutes(1);
    int result = await CancelAfterAsync(ct => InnerCallAsync(ct), timeout,
        cancellationToken);
    return result;
}

async Task<int> InnerCallAsync(CancellationToken cancellationToken)
{
    return 42;
}

1
समाधान के लिए धन्यवाद! लगता है कि आपके पास करना चाहिए timeoutCancellationमें delayTask। वर्तमान में, यदि आप रद्दीकरण करते हैं, तो इसके बजाय CancelAfterAsyncफेंक सकते हैं , कारण पहले समाप्त हो सकता है। TimeoutExceptionTaskCanceledExceptiondelayTask
एक्सलयूसर

@ AxelUser, आप सही कह रहे हैं। यह समझने के लिए कि मुझे क्या हो रहा है, इकाई परीक्षणों के समूह के साथ एक घंटे का समय लग गया :) मैंने यह मान लिया कि जब दिए गए दोनों कार्य WhenAnyएक ही टोकन द्वारा रद्द किए जाते हैं, WhenAnyतो पहला काम वापस कर देंगे। वह धारणा गलत थी। मैंने उत्तर संपादित किया है। धन्यवाद!
जोसेफ ब्लाहा

मुझे यह पता लगाने में मुश्किल समय हो रहा है कि वास्तव में इसे परिभाषित टास्क <SomeResult> फ़ंक्शन के साथ कैसे कॉल करें; किसी भी मौका आप इसे कैसे कॉल करने के लिए एक उदाहरण पर निपट सकते हैं?
झगमा २

1
@ झगग्मा, उदाहरण जोड़ा!
जोसेफ ब्लाहा

@ जोसेफब्लाह बहुत बहुत धन्यवाद! मैं अब भी धीरे-धीरे अपने सिर को लैम्ब्डा स्टाइल सिंटैक्स के इर्द-गिर्द लपेट रहा हूं, जो मेरे साथ नहीं हुआ होगा - कि लैम्बडा फंक्शन में पास होने से टोकन कैंसलअंसिंक्स के बॉडी में टास्क में पास हो जाता है। निफ्टी!
झगमा

8

संदेश और स्वचालित रद्दीकरण को संभालने के लिए एक टाइमर का उपयोग करें । जब टास्क पूरा हो जाता है, तो टाइमर को डिस्पोज़ कर दें, ताकि वे कभी आग न लगें। यहाँ एक उदाहरण है; विभिन्न मामलों को देखने के लिए टास्कले को 500, 1500 या 2500 में बदलें:

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApplication1
{
    class Program
    {
        private static Task CreateTaskWithTimeout(
            int xDelay, int yDelay, int taskDelay)
        {
            var cts = new CancellationTokenSource();
            var token = cts.Token;
            var task = Task.Factory.StartNew(() =>
            {
                // Do some work, but fail if cancellation was requested
                token.WaitHandle.WaitOne(taskDelay);
                token.ThrowIfCancellationRequested();
                Console.WriteLine("Task complete");
            });
            var messageTimer = new Timer(state =>
            {
                // Display message at first timeout
                Console.WriteLine("X milliseconds elapsed");
            }, null, xDelay, -1);
            var cancelTimer = new Timer(state =>
            {
                // Display message and cancel task at second timeout
                Console.WriteLine("Y milliseconds elapsed");
                cts.Cancel();
            }
                , null, yDelay, -1);
            task.ContinueWith(t =>
            {
                // Dispose the timers when the task completes
                // This will prevent the message from being displayed
                // if the task completes before the timeout
                messageTimer.Dispose();
                cancelTimer.Dispose();
            });
            return task;
        }

        static void Main(string[] args)
        {
            var task = CreateTaskWithTimeout(1000, 2000, 2500);
            // The task has been started and will display a message after
            // one timeout and then cancel itself after the second
            // You can add continuations to the task
            // or wait for the result as needed
            try
            {
                task.Wait();
                Console.WriteLine("Done waiting for task");
            }
            catch (AggregateException ex)
            {
                Console.WriteLine("Error waiting for task:");
                foreach (var e in ex.InnerExceptions)
                {
                    Console.WriteLine(e);
                }
            }
        }
    }
}

इसके अलावा, Async CTP एक टास्कEx.Delay विधि प्रदान करता है जो आपके लिए कार्यों में टाइमर को लपेट देगा। यह आपको टाइमर के आग लगने पर जारी रखने के लिए टास्कस्किलर सेट करने जैसी चीजों को करने के लिए अधिक नियंत्रण दे सकता है।

private static Task CreateTaskWithTimeout(
    int xDelay, int yDelay, int taskDelay)
{
    var cts = new CancellationTokenSource();
    var token = cts.Token;
    var task = Task.Factory.StartNew(() =>
    {
        // Do some work, but fail if cancellation was requested
        token.WaitHandle.WaitOne(taskDelay);
        token.ThrowIfCancellationRequested();
        Console.WriteLine("Task complete");
    });

    var timerCts = new CancellationTokenSource();

    var messageTask = TaskEx.Delay(xDelay, timerCts.Token);
    messageTask.ContinueWith(t =>
    {
        // Display message at first timeout
        Console.WriteLine("X milliseconds elapsed");
    }, TaskContinuationOptions.OnlyOnRanToCompletion);

    var cancelTask = TaskEx.Delay(yDelay, timerCts.Token);
    cancelTask.ContinueWith(t =>
    {
        // Display message and cancel task at second timeout
        Console.WriteLine("Y milliseconds elapsed");
        cts.Cancel();
    }, TaskContinuationOptions.OnlyOnRanToCompletion);

    task.ContinueWith(t =>
    {
        timerCts.Cancel();
    });

    return task;
}

वह नहीं चाहता कि वर्तमान धागा अवरुद्ध हो, अर्थात नहीं task.Wait()
चेंग चेन

@ डैनी: यह सिर्फ उदाहरण को पूरा करने के लिए था। ContinueWith के बाद आप वापस लौट सकते हैं और कार्य को चलने दे सकते हैं। मैं अपने उत्तर को और अधिक स्पष्ट करने के लिए अपडेट करूंगा।
क्वार्टरमेस्टर

2
@dtb: क्या होगा अगर आप t1 को एक टास्क <टास्क <रिजल्ट >> बना दें और फिर टास्कएक्स्टेंशन को कॉल करें। Uwwrap? आप अपने भीतर के लैम्ब्डा से t2 वापस कर सकते हैं, और आप बाद में अलिखित कार्य में निरंतरता जोड़ सकते हैं।
क्वार्टरमेस्टर

बहुत बढ़िया! यह मेरी समस्या को पूरी तरह हल करता है। धन्यवाद! मुझे लगता है कि मैं @ AS-CII द्वारा प्रस्तावित समाधान के साथ जाऊंगा, हालांकि मैं चाहता हूं कि मैं आपका उत्तर भी स्वीकार कर सकता हूं और साथ ही टास्कएक्स्टेंशन के सुझाव के लिए भी।
dtb

6

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

public static Task TimeoutAfter(this Task task, TimeSpan timeout, IScheduler scheduler)
{
        return task.ToObservable().Timeout(timeout, scheduler).ToTask();
}

अपने यूनिट टेस्ट में नीचे दिए गए कोड का उपयोग करके ऊपर का परीक्षण करें, यह मेरे लिए काम करता है

TestScheduler scheduler = new TestScheduler();
Task task = Task.Run(() =>
                {
                    int i = 0;
                    while (i < 5)
                    {
                        Console.WriteLine(i);
                        i++;
                        Thread.Sleep(1000);
                    }
                })
                .TimeoutAfter(TimeSpan.FromSeconds(5), scheduler)
                .ContinueWith(t => { }, TaskContinuationOptions.OnlyOnFaulted);

scheduler.AdvanceBy(TimeSpan.FromSeconds(6).Ticks);

आपको निम्नलिखित नामस्थान की आवश्यकता हो सकती है:

using System.Threading.Tasks;
using System.Reactive.Subjects;
using System.Reactive.Linq;
using System.Reactive.Threading.Tasks;
using Microsoft.Reactive.Testing;
using System.Threading;
using System.Reactive.Concurrency;

4

ऊपर के केवैन के जवाब का एक सामान्य संस्करण, प्रतिक्रियात्मक एक्सटेंशन का उपयोग करना।

public static Task<T> TimeoutAfter<T>(this Task<T> task, TimeSpan timeout, IScheduler scheduler)
{
    return task.ToObservable().Timeout(timeout, scheduler).ToTask();
}

वैकल्पिक समयबद्धक के साथ:

public static Task<T> TimeoutAfter<T>(this Task<T> task, TimeSpan timeout, Scheduler scheduler = null)
{
    return scheduler is null 
       ? task.ToObservable().Timeout(timeout).ToTask() 
       : task.ToObservable().Timeout(timeout, scheduler).ToTask();
}

BTW: जब एक टाइमआउट होता है, एक टाइमआउट अपवाद फेंक दिया जाएगा


0

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


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

0

मैंने Task.Delay()टास्क को महसूस किया और CancellationTokenSourceदूसरे जवाबों में तंग-ईश नेटवर्किंग लूप में अपने उपयोग के मामले के लिए थोड़ा बहुत जवाब दिया।

और हालांकि, जो होआग ने टास्क का निर्माण किया। MSDN ब्लॉग पर मेथडआउटआउट आफ्टर मेथड प्रेरणादायक था, मैं उपयोग करने से थोड़ा डर रहा थाTimeoutException ऊपर दिए गए कारण के लिए प्रवाह नियंत्रण के लिए , क्योंकि टाइमआउट की अपेक्षा अधिक बार किया जाता है।

इसलिए मैं इसके साथ गया, जो ब्लॉग में वर्णित अनुकूलन को भी संभालता है:

public static async Task<bool> BeforeTimeout(this Task task, int millisecondsTimeout)
{
    if (task.IsCompleted) return true;
    if (millisecondsTimeout == 0) return false;

    if (millisecondsTimeout == Timeout.Infinite)
    {
        await Task.WhenAll(task);
        return true;
    }

    var tcs = new TaskCompletionSource<object>();

    using (var timer = new Timer(state => ((TaskCompletionSource<object>)state).TrySetCanceled(), tcs,
        millisecondsTimeout, Timeout.Infinite))
    {
        return await Task.WhenAny(task, tcs.Task) == task;
    }
}

एक उदाहरण उपयोग मामला इस प्रकार है:

var receivingTask = conn.ReceiveAsync(ct);

while (!await receivingTask.BeforeTimeout(keepAliveMilliseconds))
{
    // Send keep-alive
}

// Read and do something with data
var data = await receivingTask;

0

एंड्रयू अर्नोट के उत्तर के कुछ संस्करण:

  1. यदि आप किसी मौजूदा कार्य की प्रतीक्षा करना चाहते हैं और यह पता लगाना चाहते हैं कि यह पूरा हो गया है या समय समाप्त हो गया है, लेकिन समय समाप्त होने पर इसे रद्द नहीं करना चाहते हैं:

    public static async Task<bool> TimedOutAsync(this Task task, int timeoutMilliseconds)
    {
        if (timeoutMilliseconds < 0 || (timeoutMilliseconds > 0 && timeoutMilliseconds < 100)) { throw new ArgumentOutOfRangeException(); }
    
        if (timeoutMilliseconds == 0) {
            return !task.IsCompleted; // timed out if not completed
        }
        var cts = new CancellationTokenSource();
        if (await Task.WhenAny( task, Task.Delay(timeoutMilliseconds, cts.Token)) == task) {
            cts.Cancel(); // task completed, get rid of timer
            await task; // test for exceptions or task cancellation
            return false; // did not timeout
        } else {
            return true; // did timeout
        }
    }
  2. यदि आप कोई कार्य प्रारंभ करना चाहते हैं और समय समाप्त होने पर कार्य को रद्द करना चाहते हैं:

    public static async Task<T> CancelAfterAsync<T>( this Func<CancellationToken,Task<T>> actionAsync, int timeoutMilliseconds)
    {
        if (timeoutMilliseconds < 0 || (timeoutMilliseconds > 0 && timeoutMilliseconds < 100)) { throw new ArgumentOutOfRangeException(); }
    
        var taskCts = new CancellationTokenSource();
        var timerCts = new CancellationTokenSource();
        Task<T> task = actionAsync(taskCts.Token);
        if (await Task.WhenAny(task, Task.Delay(timeoutMilliseconds, timerCts.Token)) == task) {
            timerCts.Cancel(); // task completed, get rid of timer
        } else {
            taskCts.Cancel(); // timer completed, get rid of task
        }
        return await task; // test for exceptions or task cancellation
    }
  3. यदि आपके पास पहले से कोई कार्य है जिसे आप रद्द करना चाहते हैं यदि कोई समय समाप्त होता है:

    public static async Task<T> CancelAfterAsync<T>(this Task<T> task, int timeoutMilliseconds, CancellationTokenSource taskCts)
    {
        if (timeoutMilliseconds < 0 || (timeoutMilliseconds > 0 && timeoutMilliseconds < 100)) { throw new ArgumentOutOfRangeException(); }
    
        var timerCts = new CancellationTokenSource();
        if (await Task.WhenAny(task, Task.Delay(timeoutMilliseconds, timerCts.Token)) == task) {
            timerCts.Cancel(); // task completed, get rid of timer
        } else {
            taskCts.Cancel(); // timer completed, get rid of task
        }
        return await task; // test for exceptions or task cancellation
    }

एक और टिप्पणी, ये संस्करण टाइमर को रद्द कर देंगे यदि समय समाप्त नहीं होता है, तो कई कॉल टाइमर को ढेर करने का कारण नहीं बनेंगे।

एस जे बी


0

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

public static async Task<bool> TryWithTimeoutAfter<TResult>(this Task<TResult> task,
    TimeSpan timeout, Action<TResult> successor)
{

    using var timeoutCancellationTokenSource = new CancellationTokenSource();
    var completedTask = await Task.WhenAny(task, Task.Delay(timeout, timeoutCancellationTokenSource.Token))
                                  .ConfigureAwait(continueOnCapturedContext: false);

    if (completedTask == task)
    {
        timeoutCancellationTokenSource.Cancel();

        // propagate exception rather than AggregateException, if calling task.Result.
        var result = await task.ConfigureAwait(continueOnCapturedContext: false);
        successor(result);
        return true;
    }
    else return false;        
}     

async Task Example(Task<string> task)
{
    string result = null;
    if (await task.TryWithTimeoutAfter(TimeSpan.FromSeconds(1), r => result = r))
    {
        Console.WriteLine(result);
    }
}    

-3

निश्चित रूप से ऐसा नहीं करते हैं, लेकिन यह एक विकल्प है अगर ... मैं एक वैध कारण के बारे में नहीं सोच सकता।

((CancellationTokenSource)cancellationToken.GetType().GetField("m_source",
    System.Reflection.BindingFlags.NonPublic |
    System.Reflection.BindingFlags.Instance
).GetValue(cancellationToken)).Cancel();
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.