বাতিলকরণ টোকেনসোর্স কখন নিষ্পত্তি করবেন?


163

ক্লাসটি CancellationTokenSourceডিসপোজেবল। রিফ্লেক্টরে একটি তাত্ক্ষণিকভাবে নজরদারি করা হয়েছে KernelEvent, একটি (খুব সম্ভবত) অব্যবহৃত রিসোর্সের ব্যবহার। যেহেতু CancellationTokenSourceকোনও চূড়ান্তকরণকারী নেই, যদি আমরা এটি নিষ্পত্তি না করি তবে জিসি এটি করবে না।

অন্যদিকে, আপনি যদি प्रबंधিত থ্রেডগুলিতে এমএসডিএন নিবন্ধ বাতিলকরণের তালিকাভুক্ত নমুনাগুলি দেখে থাকেন তবে কেবলমাত্র একটি কোড স্নিপেট টোকেনটিকে নিষ্পত্তি করে।

কোডে এটি নিষ্পত্তি করার সঠিক উপায় কী?

  1. আপনি usingযদি আপনার জন্য অপেক্ষা না করেন তবে আপনার সমান্তরাল কাজটি শুরু করে কোডটি মোড়ানো করতে পারবেন না । এবং যদি আপনি অপেক্ষা না করেন তবেই বাতিলকরণটি বোধগম্য।
  2. অবশ্যই আপনি ContinueWithকোনও Disposeকল দিয়ে কাজটিতে যুক্ত করতে পারেন , তবে কি সেই পথেই যেতে হবে?
  3. বাতিলকরণযোগ্য প্লিনকিউ প্রশ্নাগুলি সম্পর্কে কী, যা পিছনে সিঙ্ক্রোনাইজ করে না, তবে শেষে কিছু করে? বলি .ForAll(x => Console.Write(x))?
  4. এটি কি পুনরায় ব্যবহারযোগ্য? একই টোকনটি বেশ কয়েকটি কলের জন্য ব্যবহার করা যেতে পারে এবং তারপরে এটি হোস্ট উপাদানটির সাথে একসাথে নিষ্পত্তি করতে দেওয়া যাক, ইউআই নিয়ন্ত্রণ বলতে পারি?

কারণ এতে Resetপরিষ্কার-পরিচ্ছন্নতার পদ্ধতি IsCancelRequestedএবং Tokenক্ষেত্রের মতো কিছু নেই আমি মনে করি এটি পুনরায় ব্যবহারযোগ্য নয়, সুতরাং প্রতিবার আপনি যখন কোনও কাজ শুরু করেন (বা একটি পিনকিউ কোয়েরি) আপনার একটি নতুন তৈরি করা উচিত। এটা সত্যি? যদি হ্যাঁ, তবে আমার প্রশ্নটি হ'ল Disposeএই বহু CancellationTokenSourceঘটনার মোকাবেলায় সঠিক এবং প্রস্তাবিত কৌশলটি কী?

উত্তর:


82

ডিসপোজ চালু করা সত্যিই প্রয়োজন কিনা তা নিয়ে কথা CancellationTokenSourceবলছি ... আমার প্রকল্পে আমার একটি মেমরি ফাঁস হয়েছিল এবং এটি CancellationTokenSourceসমস্যাটি ছিল out

আমার প্রকল্পের একটি পরিষেবা রয়েছে, যা নিয়মিত ডাটাবেস পড়ছে এবং বিভিন্ন কাজগুলি বন্ধ করে দিচ্ছে, এবং আমি আমার কর্মীদের কাছে লিঙ্কযুক্ত বাতিলকরণ টোকেনগুলি দিয়ে যাচ্ছিলাম, সুতরাং তারা ডেটা প্রক্রিয়াকরণ শেষ করার পরেও বাতিলকরণ টোকেনগুলি নিষ্পত্তি করা হয়নি, যা একটি স্মৃতি ফাঁস হওয়ার কারণ হয়েছিল।

পরিচালিত থ্রেডগুলিতে এমএসডিএন বাতিলকরণ এটি পরিষ্কারভাবে বলেছে:

লক্ষ্য করুন যে Disposeলিঙ্কযুক্ত টোকেন উত্সটি শেষ হয়ে গেলে আপনাকে অবশ্যই কল করতে হবে । আরও সম্পূর্ণ উদাহরণের জন্য, দেখুন কীভাবে: একাধিক বাতিলকরণের অনুরোধ শুনুন

আমি ContinueWithআমার বাস্তবায়নে ব্যবহার করেছি।


14
ব্রায়ান ক্রসবি দ্বারা গৃহীত উত্তরের এটির একটি গুরুত্বপূর্ণ বাদ দেওয়া - আপনি যদি কোনও লিঙ্কযুক্ত সিটিএস তৈরি করেন তবে আপনার মেমরি ফাঁস হওয়ার ঝুঁকি রয়েছে। দৃশ্যটি ইভেন্ট হ্যান্ডলারের সাথে খুব মিল, যা কখনই নিবন্ধভুক্ত নয়।
সেরেন বোইসেন

5
এই একই সমস্যার কারণে আমার একটি ফুটো হয়েছিল। কোনও প্রোফাইলার ব্যবহার করে আমি কলব্যাক রেজিস্ট্রেশনগুলিকে লিঙ্কযুক্ত সিটিএস দৃষ্টান্তের রেফারেন্সগুলি দেখতে পেয়েছি। এখানে সিটিএস নিষ্পত্তি বাস্তবায়নের জন্য কোড পরীক্ষা করা খুব অন্তর্দৃষ্টিপূর্ণ ছিল এবং ইভেন্ট হ্যান্ডলার নিবন্ধকরণ ফাঁসের তুলনায় @ সেরেনবয়েসেনকে আন্ডারস্কোর করে।
বিটমাস্ক777

উপরের মন্তব্যগুলি আলোচনার রাজ্যের প্রতিফলন ঘটায় @ ব্রায়ান ক্রসবি দ্বারা গ্রহণ করা অন্য উত্তর ছিল।
জর্জ মামালাদজে

2020-তে ডকুমেন্টেশনটি পরিষ্কারভাবে বলেছে: Important: The CancellationTokenSource class implements the IDisposable interface. You should be sure to call the CancellationTokenSource.Dispose method when you have finished using the cancellation token source to free any unmanaged resources it holds.- ডকস.মাইক্রোসফট.ফেন
ডটনেট

44

আমি মনে করি না যে কোনও বর্তমান উত্তর সন্তোষজনক ছিল। গবেষণা করার পরে আমি স্টিফেন টুব ( রেফারেন্স ) এর এই উত্তরটি পেয়েছি :

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

এটি বাতিলকরণ টোকেনের জন্য খুব বিরল aউইটহ্যান্ডলটি ব্যবহার করা উচিত, তাই এটি পরিষ্কার করার পরে সাধারণত এটি ডিসপোজ ব্যবহারের দুর্দান্ত কারণ নয়। তবে, আপনি যদি ক্রিয়েটলিংকড টোকেনসোর্স দিয়ে আপনার সিটিএস তৈরি করছেন বা আপনি যদি সিটিএসের টাইমার কার্যকারিতা ব্যবহার করছেন তবে ডিসপোজ ব্যবহার করা আরও কার্যকর হতে পারে।

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


10
আরও কার্যকর এর অর্থ শিশু সিটিএস পিতামাতার সাথে যুক্ত করা হয়। আপনি যদি সন্তানের নিষ্পত্তি না করেন তবে পিতা-মাতার দীর্ঘজীবী হলে লিক হবে। সুতরাং এটি সংযুক্তগুলি নিষ্পত্তি করা সমালোচনামূলক।
গ্রেগরি

26

আমি ILSpy একবার দেখে নেন CancellationTokenSourceকিন্তু আমি শুধুমাত্র জানতে পারেন m_KernelEventযা আসলে একটি হয় ManualResetEvent, যা একটি একটি রাপার ক্লাস হয় WaitHandleঅবজেক্ট। এটি জিসি দ্বারা সঠিকভাবে পরিচালনা করা উচিত।


7
আমার একই অনুভূতি আছে যে জিসি সে সব পরিষ্কার করবেন। আমি তা যাচাই করার চেষ্টা করব। মাইক্রোসফ্ট কেন এই ক্ষেত্রে নিষ্পত্তি কার্যকর করেছে? ইভেন্ট কলব্যাকগুলি থেকে মুক্তি পেতে এবং সম্ভবত দ্বিতীয় প্রজন্মের জিসিতে প্রচার এড়াতে। এক্ষেত্রে ডিসপোজকে কল করা alচ্ছিক - যদি আপনি এটি করতে পারেন তবে তা কল করুন, যদি কেবল এটিকে অবহেলা না করেন। আমি মনে করি সেরা পদ্ধতি নয়।
জর্জি মামালাদজে

4
আমি এই বিষয়টি তদন্ত করেছি। বাতিলকরণ টোকেনসোর্স জঞ্জাল সংগ্রহ করে। জিন 1 জিসিতে এটি নিষ্পত্তি করতে আপনি সহায়তা করতে পারেন। স্বীকার করা হয়েছে।
জর্জ মামালাদজে

1
আমি স্বাধীনভাবে এই তদন্তটি করেছি এবং একই সিদ্ধান্তে পৌঁছেছি: আপনি সহজেই পারলে নিষ্পত্তি করুন তবে বিরল-তবে-শোনা যায় না এমন ক্ষেত্রে যেখানে আপনি বাতিলকরণ টোকেন প্রেরণ করেছেন সেখানে তা করার চেষ্টা করবেন না বুংডকস এবং তাদের পোস্টকার্ড লেখার জন্য অপেক্ষা করতে চান না তারা আপনাকে জানিয়েছে যে তারা এটি সম্পন্ন করেছে। বাতিলকরণ টোকেন কীসের জন্য ব্যবহৃত হয় তার প্রকৃতির কারণে এটি এখনই ঘটতে চলেছে, এবং এটি সত্যই ঠিক আছে, আমি প্রতিশ্রুতি দিচ্ছি।
জো অ্যামেন্টা

6
আমার উপরের মন্তব্যটি লিঙ্কযুক্ত টোকেন উত্সগুলিতে প্রযোজ্য নয়; আমি প্রমাণ করতে পারিনি যে এই নির্বিঘ্নে ছেড়ে যাওয়া ঠিক আছে, এবং এই থ্রেড এবং এমএসডিএন এর বুদ্ধি পরামর্শ দেয় যে এটি নাও হতে পারে।
জো অ্যামেন্টা

23

আপনি সর্বদা নিষ্পত্তি করা উচিত CancellationTokenSource

কীভাবে এটি নিষ্পত্তি করা যায় তা দৃশ্যের উপর নির্ভর করে। আপনি বেশ কয়েকটি ভিন্ন দৃশ্যের প্রস্তাব দিন।

  1. usingআপনি যখন CancellationTokenSourceঅপেক্ষা করেন এমন কিছু সমান্তরাল কাজ ব্যবহার করেন কেবল তখনই কাজ করে । যদি এটি আপনার সেনেরিও হয় তবে দুর্দান্ত, এটি সবচেয়ে সহজ পদ্ধতি।

  2. কার্যগুলি ব্যবহার করার সময়, কোনও ContinueWithকাজটি ব্যবহারের নিষ্পত্তি করার নির্দেশ হিসাবে আপনি ব্যবহার করুন CancellationTokenSource

  3. প্লিনিকের জন্য আপনি ব্যবহার করতে পারবেন usingযেহেতু আপনি এটি সমান্তরালভাবে চালাচ্ছেন তবে সমান্তরালভাবে চলমান সমস্ত কর্মীর জন্য অপেক্ষা করছেন।

  4. ইউআই এর জন্য, আপনি CancellationTokenSourceপ্রতিটি বাতিলযোগ্য অপারেশনের জন্য একটি নতুন তৈরি করতে পারেন যা কোনও একক বাতিল ট্রিগারটির সাথে আবদ্ধ নয়। একটি বজায় রাখুন List<IDisposable>এবং তালিকায় প্রতিটি উত্স যুক্ত করুন, যখন আপনার উপাদানগুলি নিষ্পত্তি হবে তখন সেগুলি সমস্ত নিষ্পত্তি করুন।

  5. থ্রেডগুলির জন্য, একটি নতুন থ্রেড তৈরি করুন যা সমস্ত কর্মী থ্রেডের সাথে যোগ দেয় এবং একক উত্স বন্ধ করে দেয় যখন সমস্ত কর্মী থ্রেড শেষ হয়। বাতিলকরণ টোকেনসোর্স দেখুন , কখন নিষ্পত্তি করবেন?

সবসময় একটি উপায় আছে। IDisposableউদাহরণগুলি সর্বদা নিষ্পত্তি করা উচিত। নমুনাগুলি প্রায়শই তা হয় না কারণ তারা হয় মূল ব্যবহারগুলি দেখানোর জন্য দ্রুত নমুনা বা শ্রেণীর সমস্ত দিক যুক্ত করে দেখানো হচ্ছে যে কোনও নমুনার জন্য অত্যধিক জটিল। নমুনাটি হ'ল একটি নমুনা, প্রয়োজনীয় (বা এমনকি সাধারণত) উত্পাদন মানের কোড নয়। সমস্ত নমুনা যেমন প্রডাকশন কোডে অনুলিপিযোগ্য হয় তেমন গ্রহণযোগ্য নয়।


পয়েন্ট 2 এর জন্য, কোনও কারণে আপনি awaitটাস্কটিতে ব্যবহার করতে পারেন নি এবং অপেক্ষার পরে উপস্থিত কোডটিতে বাতিলকরণ টোকেনসোর্সটি নিষ্পত্তি করতে পারেন ?
গতকাল

14
সাবধানবাণী আছে। আপনি awaitঅপারেশন চলাকালীন যদি সিটিএস বাতিল হয়ে যায়, আপনি কোনও কারণে পুনরায় শুরু করতে পারেন OperationCanceledException। আপনি তখন কল করতে পারেন Dispose()। কিন্তু যদি সেখানে অপারেশন এখনও চলমান এবং সংশ্লিষ্ট ব্যবহার করছেন CancellationToken, যে টোকেন এখনও রিপোর্ট CanBeCanceledহচ্ছে trueযদিও উৎস বিন্যস্ত করা হয়। যদি তারা একটি বাতিল কলব্যাক নিবন্ধন করার চেষ্টা করে, বুম! , ObjectDisposedExceptionDispose()অপারেশন (গুলি) এর সফল সমাপ্তির পরে কল করা এটি যথেষ্ট নিরাপদ । এটি পায় সত্যিই চতুর যখন আপনি আসলে কিছু বাতিল করা প্রয়োজন।
মাইক স্ট্রোবেল

8
মাইক স্ট্রোবেলের প্রদত্ত কারণগুলির জন্য বঞ্চিত - সর্বদা ডিসপোজ কল করতে একটি নিয়ম জোর করে সিটিএস এবং টাস্কের সাথে অ্যাসিঙ্ক্রোনাস প্রকৃতির কারণে কাজ করার সময় আপনাকে লোমশ পরিস্থিতিতে ফেলতে পারে। পরিবর্তে নিয়মটি হওয়া উচিত: সর্বদা সংযুক্ত টোকেন উত্সগুলি নিষ্পত্তি করুন ।
সেরেন বোইসেন

1
আপনার লিঙ্কটি একটি মোছা উত্তরে যায়।
21

19

গুগল অনুসন্ধানগুলিতে এই উত্তরটি এখনও আসছে এবং আমি বিশ্বাস করি যে ভোট দেওয়া উত্তর পুরো গল্পটি দেয় না। (সিটিএস) এবং (সিটি) জন্য উত্স কোডটি দেখার পরে আমি বিশ্বাস করি যে বেশিরভাগ ব্যবহারের ক্ষেত্রে নিম্নলিখিত কোড সিক্যুয়েন্সটি ভাল:CancellationTokenSourceCancellationToken

if (cancelTokenSource != null)
{
    cancelTokenSource.Cancel();
    cancelTokenSource.Dispose();
    cancelTokenSource = null;
}

দ্য m_kernelHandleঅভ্যন্তরীণ উপরে উল্লিখিত ক্ষেত্র ব্যাক সিঙ্ক্রোনাইজেশন বস্তুর WaitHandleউভয় CTS এবং সিটি ক্লাসের সম্পত্তি। আপনি যদি সেই সম্পত্তি অ্যাক্সেস করেন তবে এটি কেবল তাত্ক্ষণিক হয়। সুতরাং, যদি না আপনি WaitHandleআপনার Taskকলিং ডিসপোজে কিছু পুরানো-স্কুল থ্রেড সিঙ্ক্রোনাইজেশনের জন্য ব্যবহার না করেন তবে তার কোনও প্রভাব থাকবে না।

অবশ্যই, যদি আপনি এটি ব্যবহার করে থাকেন তবে উপরের অন্যান্য উত্তরগুলির দ্বারা প্রস্তাবিত যা করা উচিত এবং হ্যান্ডেলটি ব্যবহার করে Disposeকোনও WaitHandleক্রিয়াকলাপ সম্পূর্ণ না হওয়া অবধি কলিংয়ে বিলম্ব করা উচিত , কারণ, ওয়েটহ্যান্ডলের উইন্ডোজ এপিআই ডকুমেন্টেশনে বর্ণিত হিসাবে , ফলাফলগুলি অপরিবর্তিত রয়েছে।


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

এমএসডিএন অনুসারে, রেজিস্টার্ড কলব্যাকগুলি ব্যতিক্রম করেছে যেগুলি বাতিল করবে l আপনার কোড কল করবে না। যদি এটি ঘটে তবে মনে রাখবেন ()। কলব্যাকগুলি যাতে না হয় সে সম্পর্কে সতর্ক হওয়া উচিত, তবে এটি ঘটতে পারে।
জোসেফ লেনাক্স

11

আমি এটি জিজ্ঞাসা করেছি এবং অনেক সহায়ক উত্তর পেয়েছি এটি অনেক দিন হয়েছে তবে আমি এটি সম্পর্কিত একটি আকর্ষণীয় ইস্যুটি পেয়েছি এবং ভেবেছিলাম যে এটিকে এখানে অন্যরকম উত্তর হিসাবে পোস্ট করব:

আপনার CancellationTokenSource.Dispose()কেবল তখনই কল করা উচিত যখন আপনি নিশ্চিত হন যে কেউ সিটিএসের Tokenসম্পত্তি পাওয়ার চেষ্টা করবে না । অন্যথায় আপনি এটি কল করা উচিত নয় , কারণ এটি একটি জাতি। উদাহরণস্বরূপ, এখানে দেখুন:

https://github.com/aspnet/AspNetKatana/issues/108

এই সমস্যার সমাধানে, পূর্বে করা কোডটি cts.Cancel(); cts.Dispose();সম্পাদিত হয়েছিল কেবল cts.Cancel();কারণটি করা হয়েছিল যে তার দুর্ভাগ্যজনক যে কেউ তার বাতিল অবস্থার আহ্বান জানার পরে বাতিলকরণ টোকেন পাওয়ার চেষ্টা করার জন্য Disposeদুর্ভাগ্যক্রমে হ্যান্ডেলও করতে হবে ObjectDisposedException- এছাড়াও অতিরিক্ত OperationCanceledExceptionযে তারা জন্য পরিকল্পনা ছিল।

এই সংশোধন সম্পর্কিত আরেকটি মূল পর্যবেক্ষণ ট্র্যাচার দ্বারা তৈরি করা হয়েছে: "নিষ্পত্তি কেবল টোকেনগুলির জন্য প্রয়োজন যা বাতিল করা হবে না, কারণ বাতিলকরণ একই ক্লিনআপ সবই করে does" অর্থাত্ Cancel()ডিসপোজ করার পরিবর্তে করা সত্যিই যথেষ্ট ভাল!


1

আমি একটি থ্রেড-নিরাপদ বর্গ যে শুশ্রূষা একটি প্রণীত CancellationTokenSourceএকটি থেকে Task, এবং নিশ্চয়তা যে CancellationTokenSourceবিন্যস্ত করা হবে তার সংশ্লিষ্ট Taskসমাপ্ত হবে। এটি CancellationTokenSourceনিষ্পত্তি হওয়ার সময় বা পরে তা বাতিল হবে না তা নিশ্চিত করার জন্য এটি লকগুলি ব্যবহার করে । ডকুমেন্টেশনের সাথে সম্মতির জন্য এটি ঘটে , এতে বলা হয়েছে:

Disposeপদ্ধতিটি কেবল সময় ব্যবহৃত অন্যান্য সমস্ত কাজকর্মের হওয়া আবশ্যক CancellationTokenSourceবস্তুর সম্পন্ন করেছেন।

এবং এছাড়াও :

Disposeপদ্ধতি ছেড়ে CancellationTokenSourceএকটি অব্যবহারযোগ্য অবস্থায়।

এখানে ক্লাস:

public class CancelableExecution
{
    private readonly bool _allowConcurrency;
    private Operation _activeOperation;

    private class Operation : IDisposable
    {
        private readonly object _locker = new object();
        private readonly CancellationTokenSource _cts;
        private readonly TaskCompletionSource<bool> _completionSource;
        private bool _disposed;

        public Task Completion => _completionSource.Task; // Never fails

        public Operation(CancellationTokenSource cts)
        {
            _cts = cts;
            _completionSource = new TaskCompletionSource<bool>(
                TaskCreationOptions.RunContinuationsAsynchronously);
        }
        public void Cancel()
        {
            lock (_locker) if (!_disposed) _cts.Cancel();
        }
        void IDisposable.Dispose() // Is called only once
        {
            try
            {
                lock (_locker) { _cts.Dispose(); _disposed = true; }
            }
            finally { _completionSource.SetResult(true); }
        }
    }

    public CancelableExecution(bool allowConcurrency)
    {
        _allowConcurrency = allowConcurrency;
    }
    public CancelableExecution() : this(false) { }

    public bool IsRunning =>
        Interlocked.CompareExchange(ref _activeOperation, null, null) != null;

    public async Task<TResult> RunAsync<TResult>(
        Func<CancellationToken, Task<TResult>> taskFactory,
        CancellationToken extraToken = default)
    {
        var cts = CancellationTokenSource.CreateLinkedTokenSource(extraToken, default);
        using (var operation = new Operation(cts))
        {
            // Set this as the active operation
            var oldOperation = Interlocked.Exchange(ref _activeOperation, operation);
            try
            {
                if (oldOperation != null && !_allowConcurrency)
                {
                    oldOperation.Cancel();
                    await oldOperation.Completion; // Continue on captured context
                }
                var task = taskFactory(cts.Token); // Run in the initial context
                return await task.ConfigureAwait(false);
            }
            finally
            {
                // If this is still the active operation, set it back to null
                Interlocked.CompareExchange(ref _activeOperation, null, operation);
            }
        }
    }

    public Task RunAsync(Func<CancellationToken, Task> taskFactory,
        CancellationToken extraToken = default)
    {
        return RunAsync<object>(async ct =>
        {
            await taskFactory(ct).ConfigureAwait(false);
            return null;
        }, extraToken);
    }

    public Task CancelAsync()
    {
        var operation = Interlocked.CompareExchange(ref _activeOperation, null, null);
        if (operation == null) return Task.CompletedTask;
        operation.Cancel();
        return operation.Completion;
    }

    public bool Cancel() => CancelAsync() != Task.CompletedTask;
}

CancelableExecutionক্লাসের প্রাথমিক পদ্ধতিগুলি হ'ল RunAsyncএবং Cancel। ডিফল্টরূপে সমবর্তী ক্রিয়াকলাপ অনুমোদিত নয়, যার অর্থ কলিংRunAsync নতুন অপারেশন শুরুর আগে দ্বিতীয়বার নিঃশব্দে বাতিল এবং পূর্বের ক্রিয়াকলাপটি (এটি এখনও চলমান থাকলে) সমাপ্তির অপেক্ষায় থাকবে।

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

private readonly CancelableExecution _cancelableExecution = new CancelableExecution();

private async void btnExecute_Click(object sender, EventArgs e)
{
    string result;
    try
    {
        Cursor = Cursors.WaitCursor;
        btnExecute.Enabled = false;
        btnCancel.Enabled = true;
        result = await _cancelableExecution.RunAsync(async ct =>
        {
            await Task.Delay(3000, ct); // Simulate some cancelable I/O operation
            return "Hello!";
        });
    }
    catch (OperationCanceledException)
    {
        return;
    }
    finally
    {
        btnExecute.Enabled = true;
        btnCancel.Enabled = false;
        Cursor = Cursors.Default;
    }
    this.Text += result;
}

private void btnCancel_Click(object sender, EventArgs e)
{
    _cancelableExecution.Cancel();
}

RunAsyncপদ্ধতি একটি অতিরিক্ত গ্রহণ CancellationTokenযুক্তি, যে অভ্যন্তরীণভাবে নির্মিত লিঙ্ক করা হয়েছে যেমন CancellationTokenSource। এই alচ্ছিক টোকেন সরবরাহ করা অ্যাডভান্স দৃশ্যে কার্যকর হতে পারে।

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