অপেক্ষায় কোনও কার্য বাতিল করার পদ্ধতি কীভাবে?


164

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

private async void TryTask()
{
    CancellationTokenSource source = new CancellationTokenSource();
    source.Token.Register(CancelNotification);
    source.CancelAfter(TimeSpan.FromSeconds(1));
    var task = Task<int>.Factory.StartNew(() => slowFunc(1, 2), source.Token);

    await task;            

    if (task.IsCompleted)
    {
        MessageDialog md = new MessageDialog(task.Result.ToString());
        await md.ShowAsync();
    }
    else
    {
        MessageDialog md = new MessageDialog("Uncompleted");
        await md.ShowAsync();
    }
}

private int slowFunc(int a, int b)
{
    string someString = string.Empty;
    for (int i = 0; i < 200000; i++)
    {
        someString += "a";
    }

    return a + b;
}

private void CancelNotification()
{
}

এই নিবন্ধটি সন্ধান পেয়েছে যা বাতিল করার বিভিন্ন উপায় বুঝতে আমার সহায়তা করেছে।
উয়ে কেইম

উত্তর:


239

আপ পড়ুন বাতিলকরণ (যা .NET 4.0 চালু হয় এবং তারপর থেকে মূলত অপরিবর্তিত হয়) এবং কার্য ভিত্তিক অ্যাসিঙ্ক্রোনাস প্যাটার্ন , কীভাবে ব্যবহার করবেন তার নির্দেশিকা প্রদান CancellationTokenসঙ্গে asyncপদ্ধতি।

সংক্ষেপে বলতে CancellationTokenগেলে , আপনি প্রতিটি পদ্ধতিতে একটি পাস করে যা বাতিলকরণকে সমর্থন করে এবং সেই পদ্ধতিটি অবশ্যই পর্যায়ক্রমে এটি পরীক্ষা করা উচিত।

private async Task TryTask()
{
  CancellationTokenSource source = new CancellationTokenSource();
  source.CancelAfter(TimeSpan.FromSeconds(1));
  Task<int> task = Task.Run(() => slowFunc(1, 2, source.Token), source.Token);

  // (A canceled task will raise an exception when awaited).
  await task;
}

private int slowFunc(int a, int b, CancellationToken cancellationToken)
{
  string someString = string.Empty;
  for (int i = 0; i < 200000; i++)
  {
    someString += "a";
    if (i % 1000 == 0)
      cancellationToken.ThrowIfCancellationRequested();
  }

  return a + b;
}

2
বাহ দুর্দান্ত তথ্য! এটি নিখুঁতভাবে কাজ করেছে, এখন আমাকে অ্যাসিঙ্ক পদ্ধতিতে কীভাবে ব্যতিক্রমটি পরিচালনা করতে হবে তা বুঝতে হবে। ধন্যবাদ মানুষ! আপনার প্রস্তাবিত জিনিসগুলি আমি পড়ব।
কার্লো

8
না। বেশিরভাগ দীর্ঘমেয়াদে চলমান সিঙ্ক্রোনাস পদ্ধতিগুলিকে এগুলি বাতিল করার কিছু উপায় রয়েছে - কখনও কখনও অন্তর্নিহিত সংস্থান বন্ধ করে বা অন্য কোনও পদ্ধতিতে কল করে। CancellationTokenকাস্টম বাতিলকরণ সিস্টেমের সাথে ইন্টারপ করার জন্য প্রয়োজনীয় সমস্ত হুক রয়েছে, তবে কিছুই অপ্রয়োজনীয় পদ্ধতি বাতিল করতে পারে না।
স্টিফেন ক্লিয়ারি

1
আহ আমি দেখি. প্রসেস ক্যান্সেলড এক্সসেপশন ধরার সর্বোত্তম উপায়টি হ'ল 'অপেক্ষা' চেষ্টা করে / ধরাতে মোড়ানো? কখনও কখনও আমি সমষ্টিগত ধারণা পাই এবং আমি এটি পরিচালনা করতে পারি না।
কার্লো

3
ঠিক। আমি সুপারিশ যা আপনি ব্যবহার না Waitবা Resultasyncপদ্ধতি; awaitপরিবর্তে আপনার সর্বদা ব্যবহার করা উচিত , যা ব্যতিক্রমটিকে সঠিকভাবে মোড়ক করে।
স্টিফেন ক্লিয়ারি

11
শুধু কৌতূহলী, এর কোনও কারণ আছে যে উদাহরণগুলির CancellationToken.IsCancellationRequestedকোনওটিই ব্যবহার না করে পরিবর্তে ব্যতিক্রম ছুঁড়ে দেওয়ার পরামর্শ দেয়?
জেমস এম

41

অথবা, সংশোধন এড়াতে slowFunc(বলুন যে উদাহরণস্বরূপ উত্স কোডটিতে আপনার অ্যাক্সেস নেই):

var source = new CancellationTokenSource(); //original code
source.Token.Register(CancelNotification); //original code
source.CancelAfter(TimeSpan.FromSeconds(1)); //original code
var completionSource = new TaskCompletionSource<object>(); //New code
source.Token.Register(() => completionSource.TrySetCanceled()); //New code
var task = Task<int>.Factory.StartNew(() => slowFunc(1, 2), source.Token); //original code

//original code: await task;  
await Task.WhenAny(task, completionSource.Task); //New code

আপনি https://github.com/StephenCleary/AsyncEx থেকে দুর্দান্ত এক্সটেনশন পদ্ধতিগুলিও ব্যবহার করতে পারেন এবং এটি দেখতে এত সহজ দেখায়:

await Task.WhenAny(task, source.Token.AsTask());

1
এটি দেখতে খুব কৃপণ দেখাচ্ছে ... পুরো অসম-প্রতীক বাস্তবায়নের জন্য। আমি মনে করি না যে এই ধরনের নির্মাণগুলি উত্স কোডটি আরও পাঠযোগ্য।
ম্যাক্সিম

1
আপনাকে ধন্যবাদ, একটি নোট - নিবন্ধকরণ টোকেনটি পরে নিষ্পত্তি করা উচিত, দ্বিতীয় জিনিস - ব্যবহার করুন ConfigureAwaitঅন্যথায় আপনি ইউআই অ্যাপ্লিকেশনগুলিতে আহত হতে পারেন।
নভোচারী

@ এস্ট্রোওলকার: হ্যাঁ সত্যই টোকেনের নিবন্ধকরণ আরও নিবন্ধভুক্ত (নিষ্পত্তি) করা উচিত। এটি রেজিস্ট্রার () দ্বারা প্রত্যাবর্তিত বস্তুটির নিষ্পত্তি কল করে নিবন্ধকের কাছে পাস হওয়া প্রতিনিধিটির অভ্যন্তরে এটি করা যেতে পারে। তবে যেহেতু "উত্স" টোকেনটি কেবল এই ক্ষেত্রে স্থানীয়, তাই যাইহোক সবকিছু পরিষ্কার হয়ে যাবে ...
সোনাতিক

1
আসলে এটি যা লাগে তা হ'ল বাসা using
জ্যোতির্বিজ্ঞানী

@ এস্ট্রোওয়ালার ;-) হ্যাঁ আপনি ঠিক বলেছেন। এক্ষেত্রে এটি অনেক সহজ সমাধান! তবে, আপনি যদি টাস্কটি ফেরত নিতে চান তবে যখন কোনও সরাসরি (প্রতীক্ষা ছাড়াই) ফিরে আসতে চান তবে আপনার অন্য কিছু প্রয়োজন। আমি এটি বলছি কারণ আমি একবার এটির মতো একটি রিফ্যাক্টরিং সমস্যা হিট করেছি: আমি ব্যবহার করার আগে ... অপেক্ষা কর। আমি তখন অপেক্ষারতাকে (এবং ফাংশনটিতে অ্যাসিঙ্ক) সরিয়েছি কারণ এটি কেবলমাত্র আমিই কোডটি পুরোপুরি ভঙ্গ করছি। ফলস্বরূপ বাগটি খুঁজে পাওয়া কঠিন ছিল। আমি এইভাবে async / প্রতীক্ষার সাথে একসাথে () ব্যবহার করতে অনিচ্ছুক। আমি মনে করি ডিসপোজ প্যাটার্নটি যাইহোক
অসম্পূর্ণ

15

একটি কেস যা coveredাকেনি তা হ'ল কীভাবে একটি অ্যাসিঙ্ক পদ্ধতির অভ্যন্তরীণ বাতিলকরণ পরিচালনা করতে হয়। উদাহরণস্বরূপ একটি সাধারণ ক্ষেত্রে নিন যেখানে আপনাকে কোনও পরিষেবাতে কিছু ডেটা আপলোড করতে হবে কোনও কিছুর গণনা করার জন্য এটি পেতে এবং তারপরে কিছু ফলাফল ফিরে আসতে হবে।

public async Task<Results> ProcessDataAsync(MyData data)
{
    var client = await GetClientAsync();
    await client.UploadDataAsync(data);
    await client.CalculateAsync();
    return await client.GetResultsAsync();
}

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

public static class TaskExtensions
{
    public static async Task<T> WaitOrCancel<T>(this Task<T> task, CancellationToken token)
    {
        token.ThrowIfCancellationRequested();
        await Task.WhenAny(task, token.WhenCanceled());
        token.ThrowIfCancellationRequested();

        return await task;
    }

    public static Task WhenCanceled(this CancellationToken cancellationToken)
    {
        var tcs = new TaskCompletionSource<bool>();
        cancellationToken.Register(s => ((TaskCompletionSource<bool>)s).SetResult(true), tcs);
        return tcs.Task;
    }
}

সুতরাং এটি ব্যবহার করতে কেবল .WaitOrCancel(token)যে কোনও এসিঙ্ক কল যুক্ত করুন:

public async Task<Results> ProcessDataAsync(MyData data, CancellationToken token)
{
    Client client;
    try
    {
        client = await GetClientAsync().WaitOrCancel(token);
        await client.UploadDataAsync(data).WaitOrCancel(token);
        await client.CalculateAsync().WaitOrCancel(token);
        return await client.GetResultsAsync().WaitOrCancel(token);
    }
    catch (OperationCanceledException)
    {
        if (client != null)
            await client.CancelAsync();
        throw;
    }
}

মনে রাখবেন যে এটি আপনি যে কার্যটির জন্য অপেক্ষা করেছিলেন তা বন্ধ করবে না এবং এটি চলতে থাকবে। এটি বন্ধ করার জন্য আপনাকে একটি আলাদা প্রক্রিয়া ব্যবহার করতে হবে, CancelAsyncউদাহরণস্বরূপ কল হিসাবে বা আরও ভালভাবে এখনও এটি পাস CancellationTokenকরতে হবে Taskযাতে এটি শেষ পর্যন্ত বাতিলকরণ পরিচালনা করতে পারে। থ্রেডটি বাতিল করার চেষ্টা করা বাঞ্ছনীয় নয়


1
মনে রাখবেন যে এই কাজের জন্য অপেক্ষা করা বাতিল করার পরে, এটি আসল কাজটি বাতিল করে না (উদাহরণস্বরূপ UploadDataAsync, পটভূমিতে অবিরত থাকতে পারে, তবে এটি শেষ হলে এটি কল করবে না CalculateAsyncকারণ সেই অংশটি ইতিমধ্যে অপেক্ষারত বন্ধ করে দিয়েছে)। এটি আপনার জন্য সমস্যাযুক্ত বা নাও হতে পারে, বিশেষত যদি আপনি অপারেশনটি আবার চেষ্টা করতে চান। CancellationTokenসমস্ত উপায়ে পাস করা পছন্দসই বিকল্প, যখন সম্ভব হয়।
মিরাল

1
@ মিরাল এটি সত্য তবে অনেকগুলি অ্যাসিঙ্ক পদ্ধতি রয়েছে যা বাতিলকরণ টোকেন নেয় না। উদাহরণস্বরূপ ডাব্লুসিএফ পরিষেবাগুলি নিন, যা আপনি যখন অ্যাসিঙ্ক পদ্ধতিতে ক্লায়েন্ট তৈরি করেন তখন বাতিলকরণ টোকেনকে অন্তর্ভুক্ত করে না। উদাহরণস্বরূপ, উদাহরণটি দেখায় এবং স্টিফেন ক্লিয়ারিও উল্লেখ করেছেন যে, এটি ধরে নেওয়া হয় যে দীর্ঘকালীন চলমান সিঙ্ক্রোনাস কাজগুলিকে বাতিল করার কিছু উপায় রয়েছে।
kjbartel

1
সে কারণেই আমি বলেছিলাম "যখন সম্ভব"। বেশিরভাগ ক্ষেত্রে আমি কেবল এই ক্যাভিয়েটটির উল্লেখ করার জন্য চেয়েছিলাম যাতে পরে এই উত্তরটি খুঁজে পাওয়া লোকেরা ভুল ধারণা না পান।
মিরাল

@ মিরাল ধন্যবাদ আমি এই সতর্কতা প্রতিফলিত আপডেট করেছি।
kjbartel

দুঃখের বিষয় এটি 'নেটওয়ার্কস্ট্রিম.উইটআরেসিঙ্ক' এর মতো পদ্ধতির সাথে কাজ করে না।
জিয়োকাত

6

আমি কেবল ইতিমধ্যে গৃহীত উত্তর যুক্ত করতে চাই। আমি এতে আটকে ছিলাম, তবে সম্পূর্ণ ইভেন্টটি পরিচালনা করার জন্য আমি অন্য কোনও পথে যাচ্ছিলাম। অপেক্ষা করার পরিবর্তে, আমি কার্যটিতে একটি সম্পূর্ণ হ্যান্ডলার যুক্ত করি।

Comments.AsAsyncAction().Completed += new AsyncActionCompletedHandler(CommentLoadComplete);

যেখানে ইভেন্ট হ্যান্ডলারটি দেখতে এমন দেখাচ্ছে

private void CommentLoadComplete(IAsyncAction sender, AsyncStatus status )
{
    if (status == AsyncStatus.Canceled)
    {
        return;
    }
    CommentsItemsControl.ItemsSource = Comments.Result;
    CommentScrollViewer.ScrollToVerticalOffset(0);
    CommentScrollViewer.Visibility = Visibility.Visible;
    CommentProgressRing.Visibility = Visibility.Collapsed;
}

এই রুটের সাথে, সমস্ত হ্যান্ডলিং ইতিমধ্যে আপনার জন্য সম্পন্ন হয়েছে, যখন টাস্কটি বাতিল করা হয় কেবল ইভেন্ট হ্যান্ডলারটিকে ট্রিগার করে এবং আপনি দেখতে পারেন যে এটি সেখানে বাতিল হয়েছে কিনা।

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