অসম্পূর্ণভাবে সময় শেষ হওয়ার সাথে টাস্ক <টি> সম্পূর্ণ হওয়ার অপেক্ষা করুন


387

আমি কিছু বিশেষ নিয়ম সহ কোনও টাস্ক <টি> সম্পূর্ণ করার জন্য অপেক্ষা করতে চাই : এটি যদি এক্স মিলিসেকেন্ডের পরে শেষ না হয় তবে আমি ব্যবহারকারীর কাছে একটি বার্তা প্রদর্শন করতে চাই। এবং যদি এটি ওয়াই মিলিসেকেন্ডের পরেও শেষ না হয় তবে আমি স্বয়ংক্রিয়ভাবে বাতিল করার জন্য অনুরোধ করতে চাই ।

আমি টাস্ক.কন্টিনিউ সহ টাস্কটি সম্পূর্ণ হওয়ার জন্য অবিচ্ছিন্নভাবে অপেক্ষা করতে (উদাহরণস্বরূপ, টাস্ক শেষ হওয়ার পরে একটি ক্রিয়াকলাপ নির্ধারণের জন্য) অপেক্ষা করতে পারি, তবে এটি কোনও সময়সীমা নির্দিষ্ট করার অনুমতি দেয় না। আমি টাস্ক.ওয়েটটি সিঙ্ক্রোনালিভাবে কাজটির একটি সময়সীমা সম্পন্ন হওয়ার জন্য অপেক্ষা করতে ব্যবহার করতে পারি , তবে এটি আমার থ্রেডটিকে অবরুদ্ধ করে। আমি কীভাবে একটি সমাপ্তির সাথে টাস্কটি সম্পূর্ণ হওয়ার জন্য অবিচ্ছিন্নভাবে অপেক্ষা করতে পারি?


3
তুমি ঠিক. আমি অবাক হয়েছি এটি সময়সীমাটির জন্য সরবরাহ করে না। সম্ভবত নেট 5.0 এ ... অবশ্যই আমরা সময়সীমাটি টাস্কের মধ্যেই তৈরি করতে পারি তবে এটি কোনও ভাল নয়, এই জাতীয় জিনিসগুলি অবশ্যই ফ্রি আসতে হবে।
Aliostad

4
আপনি যে দ্বি-স্তরের টাইমআউটটি বর্ণনা করেছেন তার জন্য এটি এখনও যুক্তির প্রয়োজন হবে যদিও নেট .৪.৪ টাইমআউট-ভিত্তিক তৈরির জন্য একটি সাধারণ পদ্ধতি সরবরাহ করে CancellationTokenSource। কনস্ট্রাক্টরের দুটি ওভারলোড উপলব্ধ, একটি পূর্ণসংখ্যার মিলিসেকেন্ড বিলম্ব গ্রহণ করে এবং একটি টাইমস্প্যান দেরি করে।
পৃষ্ঠপোষক

এখানে সম্পূর্ণ সহজ liberal এর সংক্ষিপ্ত রূপ উৎস: stackoverflow.com/questions/11831844/...

সম্পূর্ণ উত্স কোড কাজ করে কোন চূড়ান্ত সমাধান? প্রতিটি থ্রেডে ত্রুটিগুলি সূচিত করার জন্য এবং ওয়েটএল পরে একটি সংক্ষিপ্তসার দেখানোর পরে আরও জটিল নমুনা হতে পারে?
কিকিনেট

উত্তর:


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
এটি উল্লেখ করা উচিত যে যদিও টাস্ক.ডেলা দীর্ঘ সময় চলমান কার্যের আগে সম্পূর্ণ করতে পারে, আপনাকে একটি সময়সীমা দৃশ্য পরিচালনা করতে দেয়, এটি দীর্ঘস্থায়ী কার্যটি নিজেই বাতিল করে না; যখনই আপনাকে সহজেই জানতে দেয় যে এতে যে কোনও একটি কাজ শেষ হয়েছে। আপনাকে একটি বাতিলকরণ টোকেন বাস্তবায়ন করতে হবে এবং দীর্ঘমেয়াদী কাজটি নিজেই বাতিল করতে হবে।
জেফ শুমাচর

30
এটিও লক্ষ করা যেতে পারে যে এই Task.Delayটাস্কটি একটি সিস্টেম টাইমার দ্বারা সমর্থিত রয়েছে যা কতক্ষণ SomeOperationAsyncসময় নেয় তা নির্বিশেষে সময়সীমা শেষ না হওয়া অবধি ট্র্যাক করা অবিরত থাকবে । সুতরাং যদি এই সামগ্রিক কোড স্নিপেটটি একটি আঁট লুপটিতে প্রচুর পরিমাণে কার্যকর করে, আপনি সমস্ত টাইমআউট না হওয়া অবধি টাইমারের জন্য সিস্টেমের সংস্থান গ্রহণ করছেন। এটির সমাধানের উপায়টি হ'ল টাইমার সংস্থানটি রিলিজ করার সময় আপনার বাতিল হওয়া CancellationTokenপাসটি হয়ে যায় । Task.Delay(timeout, cancellationToken)SomeOperationAsync
অ্যান্ড্রু অরনট

12
বাতিলকরণ কোডটি খুব বেশি কাজ করছে a এটি ব্যবহার করে দেখুন: int সময়সীমা = 1000; var বাতিলকরণ টোকেনসোর্স = নতুন বাতিলকরণ টোকেনসোর্স (সময়সীমা); var বাতিলকরণ টোকেন = টোকেনসোর্স.টোকেন; var টাস্ক = SomeOperationAsync (বাতিলকরণ টোকেন); চেষ্টা করুন task কাজের অপেক্ষায়; // সফল সমাপ্তির জন্য এখানে কোড যুক্ত করুন} ধরুন (অপারেশনক্যান্সেলড এক্সেক্সশন) {// টাইমআউট মামলার জন্য এখানে কোড যুক্ত করুন}
এসআরএম

3
@ বিল্যান্স অপেক্ষা করে 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
এই মানুষটিকে কিছু ভোট দিন। মার্জিত সমাধান। এবং যদি আপনার কলটিতে কোনও রিটার্ন টাইপ না থাকে তা নিশ্চিত করুন যে আপনি কেবল ট্র্যাসল্ট সরিয়েছেন।
লুকাস

6
বাতিলকরণ using
টোকেনসোর্স

6
@ ইট্যাট্র্যাপ কোনও কাজের জন্য দু'বার অপেক্ষা করছেন দ্বিতীয় প্রতীক্ষায় ফলাফলটি সহজেই ফিরে আসে। এটি দুবার কার্যকর করা হয় না। আপনি বলতে পারেন যে এটি task.Result দুবার কার্যকর করার সময় সমান ।
এম মিম্পেন

7
taskসময়সীমা শেষ হওয়ার পরেও মূল কাজটি ( ) চালিয়ে যেতে থাকবে?
জাগ

6
গৌণ উন্নতির সুযোগ: TimeoutExceptionএকটি উপযুক্ত ডিফল্ট বার্তা রয়েছে। "ক্রিয়াকলাপের সময় শেষ হয়ে গেছে" দিয়ে এটিকে ওভাররাইড করা। কোনও মূল্য যুক্ত করে না এবং প্রকৃতপক্ষে এটিকে ওভাররাইড করার কারণ রয়েছে বলে বোঝিয়ে কিছু বিভ্রান্তি সৃষ্টি করে।
এডওয়ার্ড ব্রে

49

আপনি Task.WaitAnyএকাধিক কাজের প্রথম অপেক্ষা করতে ব্যবহার করতে পারেন ।

আপনি দুটি অতিরিক্ত টাস্ক তৈরি করতে পারেন (যা নির্দিষ্ট টাইমআউটগুলির পরে সম্পূর্ণ) এবং তারপরে WaitAnyযাহা প্রথমটি সম্পন্ন করে তার অপেক্ষা করার জন্য ব্যবহার করতে পারেন । প্রথমে যে কাজটি সম্পন্ন হয়েছে তা যদি আপনার "কাজ" টাস্ক হয় তবে আপনি হয়ে গেছেন। প্রথম কাজটি যদি সমাপ্ত হয় তবে সময়সীমা সম্পন্ন কাজ হয়, তবে আপনি সময়সীমা সম্পর্কে প্রতিক্রিয়া জানাতে পারেন (উদাহরণস্বরূপ বাতিলকরণের অনুরোধ)।


1
আমি এমভিপি দ্বারা ব্যবহৃত এই কৌশলটি দেখেছি যার প্রতি আমি সত্যই শ্রদ্ধা করি, এটি আমার কাছে গৃহীত উত্তরের চেয়ে অনেক বেশি পরিচ্ছন্ন বলে মনে হয়। সম্ভবত একটি উদাহরণ আরও ভোট পেতে সাহায্য করবে! আমি এটি করতে স্বেচ্ছাসেবক থাকি যদি আমার পক্ষে যথেষ্ট
কার্যকারিতা

3
একটি থ্রেড অবরুদ্ধ করা হবে - তবে যদি আপনার ঠিক আছে তবে সমস্যা নেই। আমি যে সমাধানটি নিয়েছিলাম সেটি নীচের একটি, যেহেতু কোনও থ্রেড ব্লক করা হয়নি। আমি ব্লগ পোস্টটি পড়েছিলাম যা সত্যই ভাল ছিল।
জেজেস্ক

@ জেজেছক আপনি উল্লেখ করেছেন যে আপনি সমাধানটি নিয়েছেন below.... এটি কোনটি? অর্ডারের ভিত্তিতে?
BozoJoe

এবং যদি আমি ধীর কাজটি বাতিল করতে না চাই? এটি শেষ হয়ে গেলে আমি হ্যান্ডেল করতে চাই তবে বর্তমান পদ্ধতি থেকে ফিরে আসছি ..
আকমল সালিখভ

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();
    }

আপনি অন্য টাস্ক ব্যবহার করে মূল থ্রেডটি ব্লক না করেই টাস্ক.ওয়েট বিকল্পটি ব্যবহার করতে পারেন।


বাস্তবে এই উদাহরণে আপনি টি 1 এর ভিতরে অপেক্ষা করছেন না তবে একটি উচ্চতর টাস্কে আছেন on আমি আরও বিস্তারিত উদাহরণ দেওয়ার চেষ্টা করব।
as-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

এই উত্তরটি কেবলমাত্র তৃতীয় পক্ষের লাইব্রেরি গ্রন্থাগারগুলির সাথে ডিল করার সময় প্রযোজ্য যা আপনি কেবল টাইমআউট প্যারামিটার অন্তর্ভুক্ত করতে রিফ্যাক্টর করতে পারবেন না।

কিভাবে শক্ত কোড লিখতে হয়

আপনি যদি দৃust় কোডটি লিখতে চান তবে সাধারণ নিয়মটি হ'ল:

প্রতিটি একক ক্রিয়াকলাপ যা সম্ভাব্যভাবে অনির্দিষ্টকালের জন্য অবরুদ্ধ করতে পারে, তার একটি সময়সীমা থাকতে হবে।

আপনি যদি এই নিয়মটি পর্যবেক্ষণ না করেন তবে আপনার কোডটি শেষ পর্যন্ত কোনও ক্রিয়াকলাপকে আঘাত করবে যা কোনও কারণে ব্যর্থ হয়, তবে এটি অনির্দিষ্টকালের জন্য ব্লক হয়ে যাবে এবং আপনার অ্যাপ্লিকেশনটি স্থায়ীভাবে স্থায়ীভাবে স্থগিত হয়ে গেছে।

যদি কিছু সময়ের পরে যুক্তিসঙ্গত সময়সীমা থাকে তবে আপনার অ্যাপ্লিকেশনটি কিছু চরম পরিমাণে (যেমন 30 সেকেন্ড) স্থির থাকবে তবে তা হয় ত্রুটি প্রদর্শন করবে এবং এর আনন্দময় পথে চালিয়ে যাবে, বা আবার চেষ্টা করবে।


11

স্টিফেন ক্লিয়ারির দুর্দান্ত অ্যাসিনেক্সেক্স লাইব্রেরি ব্যবহার করে আপনি এটি করতে পারেন:

TimeSpan timeout = TimeSpan.FromSeconds(10);

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

TaskCanceledException সময়সীমা নির্ধারণের ইভেন্টে ফেলে দেওয়া হবে।


10

এটি পূর্বের উত্তরের কিছুটা বর্ধিত সংস্করণ।

  • লরেন্সের উত্তর ছাড়াও , সময়সীমা শেষ হওয়ার পরে এটি মূল কাজটি বাতিল করে।
  • থেকে addtion সালে sjb এর উত্তর রূপগুলো 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
এক্সেল ইউজার

এক্সেল ইউজার, আপনি ঠিক বলেছেন কী ঘটছে তা বুঝতে আমার একগুচ্ছ ইউনিট পরীক্ষাগুলি নিয়ে সময় লেগেছে :) আমি ধরে নিয়েছিলাম যে যখন দেওয়া উভয় টাস্ক WhenAnyএকই টোকেন দ্বারা বাতিল হয়ে WhenAnyযাবে তখন প্রথম কাজটি ফিরে আসবে। অনুমানটি ভুল ছিল। আমি উত্তর সম্পাদনা করেছি। ধন্যবাদ!
জোসেফ ব্লাহা

সংজ্ঞায়িত টাস্ক <সোমার রেজাল্ট> ফাংশন দিয়ে কীভাবে এটি কল করতে পারি তা নির্ধারণ করার জন্য আমার খুব কষ্ট হচ্ছে; কোনও সুযোগ আপনি কীভাবে এটি কল করতে পারেন তার একটি উদাহরণ ব্যবহার করতে পারেন?
jhaagsma

1
@ ঝাগসমা, উদাহরণ যুক্ত!
জোসেফ ব্লাহা

@ জোসেফ ব্লিহাহা অনেক ধন্যবাদ! আমি এখনও ধীরে ধীরে আমার মাথাটি ল্যাম্বদা স্টাইল সিনট্যাক্সের চারপাশে আবৃত করছি, যা আমার কাছে ঘটেনি would যে টোকেনটি ল্যাম্বডা ফাংশনে পাস করে বাতিলআফটারএেন্সিকের শরীরে টাস্কটিতে চলে গেছে। নিফটি!
jhaagsma

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 একটি TaskEx.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()
চেং চেন

@ ড্যানি: এটি কেবল উদাহরণটি সম্পূর্ণ করার জন্য। চালিয়ে যাওয়ার পরে আপনি ফিরে আসতে পারেন এবং টাস্কটি চালাতে দিন। আমি আরও উত্তর পরিষ্কার করতে আমার উত্তর আপডেট করব।
কোয়ার্টারমিস্টার

2
@ ডিটিবি: আপনি যদি টি 1 কে একটি টাস্ক <টাস্ক <ফলাফল>> করেন এবং তারপরে টাস্কএক্সটেনশানগুলিকে কল করুন nআরন্যাপ? আপনি আপনার অভ্যন্তরের ল্যাম্বদা থেকে টি 2 ফিরে আসতে পারেন, এবং আপনি পরে মোড়ানো মোড়কটিতে ক্রমাগত যুক্ত করতে পারেন।
কোয়ার্টারমিস্টার

অসাধারণ! এটি পুরোপুরি আমার সমস্যা সমাধান করে। ধন্যবাদ! আমি মনে করি আমি @ AS-CII এর প্রস্তাবিত সমাধানটি নিয়ে যাব, যদিও আমি আশা করি আমি আপনার উত্তরটিও টাস্ক এক্সটেনশনের পরামর্শ দেওয়ার জন্য গ্রহণ করতে পারব Uআরউপ করে কি আমি একটি নতুন প্রশ্ন খোলাম যাতে আপনি প্রাপ্য এই প্রতিনিধিটি পেতে পারেন?
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();
}

Alচ্ছিক সময়সূচী সহ:

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();
}

বিটিডাব্লু: যখন একটি টাইমআউট হয়, একটি সময়সীমা ব্যতিক্রম নিক্ষেপ করা হবে


0

আপনি যদি কাজটি নির্ধারণের জন্য ব্লকিংক্লাকশন ব্যবহার করেন তবে নির্মাতা সম্ভাব্য দীর্ঘ সময় ধরে চলমান কাজটি চালাতে পারবেন এবং ভোক্তা ট্রাইটেক পদ্ধতিটি ব্যবহার করতে পারবেন যার মধ্যে সময়সীমা এবং বাতিলকরণ টোকেন অন্তর্নির্মিত রয়েছে।


আমাকে কিছু লিখতে হবে (এখানে স্বত্বাধিকারী কোড রাখতে চান না) তবে দৃশ্যটি এরকম। প্রযোজক হ'ল কোডটি কার্যকর হবে যা পদ্ধতিটি কার্যকর করতে পারে এবং সময়টি কাতারে ফেলে দেয় when ভোক্তা টাইমআউট সহ ট্রিটকে () কল করবে এবং সময় শেষ হওয়ার সাথে সাথে টোকেন গ্রহণ করবে। প্রযোজক এবং গ্রাহক উভয়ই ব্যাকগ্রাউন্ড কাজগুলি হবে এবং প্রয়োজনে ইউআই থ্রেড প্রেরণকারী ব্যবহার করে ব্যবহারকারীকে একটি বার্তা প্রদর্শন করবে।
kns98

0

Task.Delay()টাস্ক CancellationTokenSource-ইশ নেটওয়ার্কিং লুপটিতে আমার ব্যবহারের ক্ষেত্রে আমি টাস্কটি অনুভব করেছি এবং অন্য উত্তরগুলিতে কিছুটা উত্তর দেওয়া হয়েছে।

এবং জো হোগের এমএসডিএন ব্লগগুলিতে টাস্কের সময়সীমার পরে তৈরি করার পদ্ধতিটি অনুপ্রেরণামূলক ছিল, তবে আমি ব্যবহারে কিছুটা ক্লান্ত ছিলাম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
    }

আর একটি মন্তব্য, এই সংস্করণগুলি টাইমার বাতিল করে যদি টাইমআউটটি ঘটে না, তাই একাধিক কলগুলির ফলে টাইমারগুলি পাইল করার কারণ হবে না।

sjb


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();
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.