मैं TPL कार्य को कैसे रद्द / रद्द कर सकता हूँ?


156

एक सूत्र में, मैं कुछ बनाता हूं System.Threading.Taskऔर प्रत्येक कार्य शुरू करता हूं ।

जब मैं .Abort()थ्रेड को मारने के लिए करता हूं , तो कार्य निरस्त नहीं किए जाते हैं।

मैं .Abort()अपने कार्यों को कैसे प्रसारित कर सकता हूं ?


जवाबों:


228

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

class Program
{
    static void Main()
    {
        var ts = new CancellationTokenSource();
        CancellationToken ct = ts.Token;
        Task.Factory.StartNew(() =>
        {
            while (true)
            {
                // do some heavy work here
                Thread.Sleep(100);
                if (ct.IsCancellationRequested)
                {
                    // another thread decided to cancel
                    Console.WriteLine("task canceled");
                    break;
                }
            }
        }, ct);

        // Simulate waiting 3s for the task to complete
        Thread.Sleep(3000);

        // Can't wait anymore => cancel this task 
        ts.Cancel();
        Console.ReadLine();
    }
}

5
अच्छी व्याख्या। मेरे पास एक प्रश्न है, टास्क में कैसे एक अनाम पद्धति नहीं है, यह कैसे काम करता है। जैसे Task.Factory.StartNew (() => ProcessMyMethod (), cancellationToken)
Prerak K

61
क्या होगा यदि कोई अवरुद्ध कॉल है जो निष्पादन कार्य के अंदर वापस नहीं आता है?
mehmet6parmak

3
@ mehmet6parmak मुझे लगता है कि केवल एक चीज जो आप कर सकते हैं, वह है Task.Wait(TimeSpan / int)इसे बाहर से समय (समय-आधारित) देने के लिए उपयोग करना।
मार्क

2
क्या होगा यदि मेरे पास एक नए तरीके के निष्पादन के प्रबंधन के लिए मेरा कस्टम वर्ग है Task? की तरह कुछ: public int StartNewTask(Action method)StartNewTaskमेरे Taskद्वारा बनाई गई विधि के अंदर Task task = new Task(() => method()); task.Start();:। तो मैं कैसे प्रबंधित कर सकता हूं CancellationToken? मैं यह भी जानना चाहूंगा कि क्या Threadमुझे एक तर्क को लागू करने की आवश्यकता है अगर यह जांचने के लिए कि क्या कुछ टास्क हैं जो अभी भी लटके हुए हैं और इसलिए जब उन्हें मारना है Form.Closing। के साथ Threadsमैं का उपयोग करें Thread.Abort()
चेशायर कैट

ओमग, क्या बुरा उदाहरण है! यह एक साधारण बूलियन स्थिति है, निश्चित रूप से यह पहला है जो कोई भी कोशिश करेगा! लेकिन यानी मेरा एक अलग टास्क में एक फंक्शन है, जिसे खत्म होने में ज्यादा समय लग सकता है, और आदर्श रूप से इसे थ्रेडिंग या जो भी हो, उसके बारे में कुछ भी पता नहीं होना चाहिए। तो मैं आपकी सलाह से फ़ंक्शन को कैसे रद्द करूं?
हाय-एंजेल

32

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

void Main()
{
    Thread thread = null;

    Task t = Task.Run(() => 
    {
        //Capture the thread
        thread = Thread.CurrentThread;

        //Simulate work (usually from 3rd party code)
        Thread.Sleep(1000);

        //If you comment out thread.Abort(), then this will be displayed
        Console.WriteLine("Task finished!");
    });

    //This is needed in the example to avoid thread being still NULL
    Thread.Sleep(10);

    //Cancel the task by aborting the thread
    thread.Abort();
}

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


2
इस विचार के लिए धन्यवाद। कुछ बाहरी कोड के लिए टाइमआउट लागू करने के लिए इस दृष्टिकोण का उपयोग किया, जिसका कोई CancellationTokenसमर्थन नहीं है ...
क्रिस्टोफ़ फ़िंक

7
AFAIK thread.abort आपको अपने स्टैक के बारे में अज्ञात छोड़ देगा, यह अमान्य हो सकता है। मैंने कभी इसकी कोशिश नहीं की है, लेकिन मुझे लगता है कि अलग ऐप डोमेन में एक धागा शुरू करना है जो थ्रेड .abort को सेव करेगा! इसके अलावा, एक कार्य को समाप्त करने के लिए आपके समाधान में एक पूरा धागा बर्बाद हो जाता है। आपको कार्यों का उपयोग नहीं करना होगा, लेकिन पहले स्थान पर धागे होंगे। (
डाउनवोट

1
जैसा कि मैंने लिखा - यह समाधान एक अंतिम उपाय है जिसे कुछ परिस्थितियों में माना जा सकता है। बेशक CancellationTokenया एक और भी सरल समाधान है कि दौड़-हालत मुक्त हैं पर विचार किया जाना चाहिए। उपरोक्त कोड केवल विधि का वर्णन करता है, उपयोग का क्षेत्र नहीं।
फ्लोरियन रैपल

8
मुझे लगता है कि इस दृष्टिकोण के अज्ञात परिणाम हो सकते हैं और मैं इसे उत्पादन कोड में अनुशंसित नहीं करूंगा। कार्य को हमेशा एक अलग धागे में निष्पादित करने की गारंटी नहीं होती है, जिसका अर्थ है कि उन्हें उसी धागे में चलाया जा सकता है जो उन्हें बनाया गया था यदि अनुसूचक ऐसा तय करता है (जिसका अर्थ है कि मुख्य धागा threadस्थानीय चर में पारित हो जाएगा )। आपके कोड में आप मुख्य धागे को समाप्त कर सकते हैं, जो कि आप वास्तव में नहीं चाहते हैं। शायद एक जाँच करें कि क्या थ्रेड्स गर्भपात से पहले के समान हैं, अगर आप गर्भपात करने के लिए जोर देते हैं, तो अच्छा होगा
Ivaylo Slavov

10
@ मार्टिन - जैसा कि मैंने एसओ पर इस प्रश्न पर शोध किया है, मैंने देखा है कि आपने थ्रेड का उपयोग करने वाले कई उत्तरों को डाउन-वोट किया है जो कार्यों को मारने के लिए हैं, लेकिन आपने कोई वैकल्पिक समाधान प्रदान नहीं किया है। आप 3 पार्टी कोड को कैसे मार सकते हैं जो रद्दीकरण का समर्थन नहीं करता है और टास्क में चल रहा है ?
गॉर्डन बीन

32

जैसा कि इस पोस्ट से पता चलता है, यह निम्नलिखित तरीके से किया जा सकता है:

int Foo(CancellationToken token)
{
    Thread t = Thread.CurrentThread;
    using (token.Register(t.Abort))
    {
        // compute-bound work here
    }
}

यद्यपि यह काम करता है, यह इस तरह के दृष्टिकोण का उपयोग करने के लिए अनुशंसित नहीं है। यदि आप उस कोड को नियंत्रित कर सकते हैं जो कार्य में निष्पादित होता है, तो आप रद्दीकरण की उचित हैंडलिंग के साथ बेहतर होंगे।


6
अपनी कमियां बताते हुए एक अलग दृष्टिकोण देने के लिए +1। मुझे नहीं पता था कि यह किया जा सकता है :)
जोएल

1
समाधान के लिए धन्यवाद! हम बस टोकन विधि को पास कर सकते हैं और टोकन को रद्द कर सकते हैं, बजाय किसी तरह विधि से धागा उदाहरण प्राप्त करने और सीधे इस उदाहरण को समाप्त करने के।
सैम

टास्क का थ्रेड जो निरस्त हो जाता है, वह थ्रेड-पूल थ्रेड हो सकता है या कॉल करने वाले थ्रेड का कार्य हो सकता है। इससे बचने के लिए, आप कार्य के लिए एक समर्पित थ्रेड निर्दिष्ट करने के लिए टास्कसाइडर का उपयोग कर सकते हैं
एडवर्ड ब्रे

19

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

कहा जा रहा है, आपको एक साझा रद्दीकरण संकेतक प्रदान करने की आवश्यकता है जो एक थ्रेड सेट करता है और प्रतीक्षा करता है जबकि दूसरा थ्रेड समय-समय पर जाँच और अनुग्रह से बाहर निकलता है। नेट 4, एक संरचना इस उद्देश्य के लिए विशेष रूप से डिजाइन शामिल हैं CancellationToken


8

आपको इसे सीधे करने की कोशिश नहीं करनी चाहिए। रद्द करने के लिए अपने कार्यों को डिज़ाइन करें रद्द करें , और उन्हें इस तरह रद्द करें।

इसके अलावा, मैं रद्द करने के लिए अपने मुख्य सूत्र को रद्द करने की सलाह दूंगा। कॉलिंग Thread.Abort()एक बुरा विचार है - इससे विभिन्न समस्याएं हो सकती हैं जिनका निदान करना बहुत मुश्किल है। इसके बजाय, वह धागा उसी रद्दीकरण का उपयोग कर सकता है जिसे आपके कार्य उपयोग करते हैं - और उसी CancellationTokenSourceका उपयोग आपके सभी कार्यों और आपके मुख्य धागे को रद्द करने के लिए किया जा सकता है ।

यह एक सरल, और सुरक्षित, डिजाइन को जन्म देगा।


8

टास्क फैक्ट्री.टार्टन्यू () में अनाम विधि का उपयोग नहीं करने पर कैंसेलेशनटॉकेन्स का उपयोग करने के तरीके के बारे में Prerak K के प्रश्न का उत्तर देने के लिए, आप MSN उदहारण में दिखाए गए तरीके से एक पैरामीटर के रूप में CancellationToken पास करें, जैसा कि MSDN उदाहरण में दिखाया गया है। यहाँ

जैसे

var tokenSource = new CancellationTokenSource();
var token = tokenSource.Token;

Task.Factory.StartNew( () => DoSomeWork(1, token), token);

static void DoSomeWork(int taskNum, CancellationToken ct)
{
    // Do work here, checking and acting on ct.IsCancellationRequested where applicable, 

}

7

मैं एक कार्य को रद्द करने के लिए एक मिश्रित दृष्टिकोण का उपयोग करता हूं।

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

नीचे एक उदाहरण देखें:

private CancellationTokenSource taskToken;
private AutoResetEvent awaitReplyOnRequestEvent = new AutoResetEvent(false);

void Main()
{
    // Start a task which is doing nothing but sleeps 1s
    LaunchTaskAsync();
    Thread.Sleep(100);
    // Stop the task
    StopTask();
}

/// <summary>
///     Launch task in a new thread
/// </summary>
void LaunchTaskAsync()
{
    taskToken = new CancellationTokenSource();
    Task.Factory.StartNew(() =>
        {
            try
            {   //Capture the thread
                runningTaskThread = Thread.CurrentThread;
                // Run the task
                if (taskToken.IsCancellationRequested || !awaitReplyOnRequestEvent.WaitOne(10000))
                    return;
                Console.WriteLine("Task finished!");
            }
            catch (Exception exc)
            {
                // Handle exception
            }
        }, taskToken.Token);
}

/// <summary>
///     Stop running task
/// </summary>
void StopTask()
{
    // Attempt to cancel the task politely
    if (taskToken != null)
    {
        if (taskToken.IsCancellationRequested)
            return;
        else
            taskToken.Cancel();
    }

    // Notify a waiting thread that an event has occurred
    if (awaitReplyOnRequestEvent != null)
        awaitReplyOnRequestEvent.Set();

    // If 1 sec later the task is still running, kill it cruelly
    if (runningTaskThread != null)
    {
        try
        {
            runningTaskThread.Join(TimeSpan.FromSeconds(1));
        }
        catch (Exception ex)
        {
            runningTaskThread.Abort();
        }
    }
}

4

निरस्तीकरण टोकन के माध्यम से रद्द करने के लिए कार्य में प्रथम श्रेणी का समर्थन है । रद्दीकरण टोकन के साथ अपने कार्य बनाएं, और इन के माध्यम से कार्यों को स्पष्ट रूप से रद्द करें।


4

आप CancellationTokenनियंत्रण का उपयोग कर सकते हैं कि क्या कार्य रद्द हो गया है। क्या आप इसे शुरू करने से पहले इसे खत्म करने के बारे में बात कर रहे हैं ("कोई बात नहीं, मैंने पहले ही यह कर लिया है"), या वास्तव में इसे बीच में रोकना? यदि पूर्व,CancellationToken सहायक हो सकता है; यदि बाद में, आपको संभवतः अपने स्वयं के "बेल आउट" तंत्र को लागू करने और कार्य निष्पादन में उचित बिंदुओं पर जांच करने की आवश्यकता होगी कि क्या आपको तेजी से विफल होना चाहिए (आप अभी भी रद्दीकरण का उपयोग कर सकते हैं आपकी मदद करने के लिए, लेकिन यह थोड़ा और अधिक मैनुअल है)।

MSDN के पास कार्य रद्द करने के बारे में एक लेख है: http://msdn.microsoft.com/en-us/library/dd997396.aspx


3

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


1

मैंने कोशिश की CancellationTokenSourceलेकिन मैं ऐसा नहीं कर सकता। और मैंने अपने तरीके से ऐसा किया। और यह काम करता है।

namespace Blokick.Provider
{
    public class SignalRConnectProvider
    {
        public SignalRConnectProvider()
        {
        }

        public bool IsStopRequested { get; set; } = false; //1-)This is important and default `false`.

        public async Task<string> ConnectTab()
        {
            string messageText = "";
            for (int count = 1; count < 20; count++)
            {
                if (count == 1)
                {
                //Do stuff.
                }

                try
                {
                //Do stuff.
                }
                catch (Exception ex)
                {
                //Do stuff.
                }
                if (IsStopRequested) //3-)This is important. The control of the task stopping request. Must be true and in inside.
                {
                    return messageText = "Task stopped."; //4-) And so return and exit the code and task.
                }
                if (Connected)
                {
                //Do stuff.
                }
                if (count == 19)
                {
                //Do stuff.
                }
            }
            return messageText;
        }
    }
}

और कॉलिंग विधि का एक और वर्ग:

namespace Blokick.Views
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class MessagePerson : ContentPage
    {
        SignalRConnectProvider signalR = new SignalRConnectProvider();

        public MessagePerson()
        {
            InitializeComponent();

            signalR.IsStopRequested = true; // 2-) And this. Make true if running the task and go inside if statement of the IsStopRequested property.

            if (signalR.ChatHubProxy != null)
            {
                 signalR.Disconnect();
            }

            LoadSignalRMessage();
        }
    }
}

0

आप किसी कार्य को थ्रेड की तरह निरस्त कर सकते हैं यदि आप कार्य को अपने स्वयं के थ्रेड पर बनाने और Abortइसके कॉल करने का कारण बन सकते हैंThread ऑब्जेक्ट । डिफ़ॉल्ट रूप से, एक कार्य थ्रेड पूल थ्रेड या कॉलिंग थ्रेड पर चलता है - जिनमें से कोई भी आप आमतौर पर गर्भपात नहीं करना चाहते हैं।

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

इस कार्य शेड्यूलर का उपयोग करें:

class SingleThreadTaskScheduler : TaskScheduler
{
    public Thread TaskThread { get; private set; }

    protected override void QueueTask(Task task)
    {
        TaskThread = new Thread(() => TryExecuteTask(task));
        TaskThread.Start();
    }

    protected override IEnumerable<Task> GetScheduledTasks() => throw new NotSupportedException(); // Unused
    protected override bool NotSupportedException(Task task, bool taskWasPreviouslyQueued) => throw new NotSupportedException(); // Unused
}

अपना काम इस तरह शुरू करें:

var scheduler = new SingleThreadTaskScheduler();
var task = Task.Factory.StartNew(action, cancellationToken, TaskCreationOptions.LongRunning, scheduler);

बाद में, आप गर्भपात कर सकते हैं:

scheduler.TaskThread.Abort();

ध्यान दें कि एक धागा गर्भपात के बारे में चेतावनी अभी भी लागू होती है:

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


यह कोड रनटाइम अपवाद के साथ विफल हो जाता
थियोडोर जूलियास

1
@ TheodorZoulias अच्छा कैच। धन्यवाद। मैंने कोड तय किया और आम तौर पर उत्तर में सुधार किया।
एडवर्ड ब्रे

1
हाँ, यह बग तय की। एक और चेतावनी है कि यह संभवतः उल्लेख किया जाना चाहिए कि Thread.Abort.NET कोर पर समर्थित नहीं है। इसका उपयोग करने का प्रयास एक अपवाद के रूप में होता है: System.PlatformNotSupportedException: थ्रेड एबोर्ट इस प्लेटफ़ॉर्म पर समर्थित नहीं है। एक तीसरा चेतावनी यह है कि प्रतिनिधियों SingleThreadTaskSchedulerके साथ बनाए गए कार्यों के साथ, दूसरे शब्दों में, वादा-शैली के कार्यों के साथ प्रभावी ढंग से उपयोग नहीं किया जा सकता है async। उदाहरण के लिए await Task.Delay(1000)कोई धागा में एक एम्बेडेड रन, इसलिए यह अप्रभावित होना धागा घटना है।
थियोडोर जूलियास
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.