web-dev-qa-db-ja.com

TPLタスクを中止/キャンセルするにはどうすればよいですか?

スレッドで、いくつかのSystem.Threading.Taskを作成し、各タスクを開始します。

.Abort()を実行してスレッドを強制終了しても、タスクは中止されません。

.Abort()をタスクに送信するにはどうすればよいですか?

140

できません。タスクは、スレッドプールのバックグラウンドスレッドを使用します。また、Abortメソッドを使用してスレッドをキャンセルすることはお勧めしません。キャンセルトークンを使用してタスクをキャンセルする適切な方法を説明する ブログの投稿をフォロー をご覧ください。以下に例を示します。

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

タスクが実行されているスレッドをキャプチャすると、タスクの中止が簡単に可能になります。これを示すサンプルコードを次に示します。

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

Task.Run()を使用して、このための最も一般的なユースケースを示しました。CancellationTokenSourceクラスを使用せずにキャンセルすべきかどうかを決定しない古いシングルスレッドコードでのタスクの快適さを使用します。

27
Florian Rappl

この投稿 が示唆するように、これは次の方法で実行できます。

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

動作しますが、そのようなアプローチを使用することは推奨されません。タスクで実行するコードを制御できる場合は、キャンセルを適切に処理することをお勧めします。

26
starteleport

このようなことは、Abortが推奨されない理由の1つです。何よりもまず、Thread.Abort()を使用して、可能な限りスレッドをキャンセルまたは停止しないでください。Abort()は、タイムリーに停止するためのより平和的な要求に対応する。

つまり、一方のスレッドが定期的にチェックして正常に終了する間、一方のスレッドが設定して待機する共有キャンセルインジケーターを提供する必要があります。 .NET 4には、この目的のために特別に設計された構造 CancellationToken が含まれています。

17
Adam Robinson

これを直接やろうとしないでください。 CancellationToken で動作するようにタスクを設計し、この方法でキャンセルします。

さらに、CancelationToken経由でも機能するようにメインスレッドを変更することをお勧めします。 Thread.Abort()を呼び出すのは悪い考えです。診断が非常に難しいさまざまな問題につながる可能性があります。代わりに、そのスレッドは、タスクが使用するのと同じ キャンセル を使用できます。また、同じCancellationTokenSourceを使用して、allタスクとメインスレッド。

これにより、はるかにシンプルで安全な設計が可能になります。

7
Reed Copsey

Task.Factory.StartNew()で匿名メソッドを使用しないときにCancellationTokensを使用する方法に関するPrera​​k Kの質問に答えるには、MSDNの例に示すように、StartNew()で開始するメソッドにパラメーターとしてCancellationTokenを渡します。 ここ

例えば.

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
Jools

混合アプローチを使用してタスクをキャンセルします。

  • まず、 Cancellation を使用して丁寧にキャンセルしようとしています。
  • まだ実行中の場合(開発者のミスなど)、不正な動作をして、古い学校の Abort メソッドを使用して強制終了します。

以下の例をご覧ください。

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

CancellationTokenを使用して、タスクをキャンセルするかどうかを制御できます。開始する前に中止すること(「気にしないで、すでにこれをしました」)について話しているのですか、それとも実際に途中で中断するのですか?前者の場合、CancellationTokenが役立ちます。後者の場合は、おそらく独自の「救済」メカニズムを実装し、タスク実行の適切な時点で高速に失敗するかどうかを確認する必要があります(CancelationTokenを使用して手伝ってもかまいませんが、もう少し手作業です)。

MSDNには、タスクのキャンセルに関する記事があります。 http://msdn.Microsoft.com/en-us/library/dd997396.aspx

3
Hank

タスクはThreadPoolで実行されているため(少なくとも、デフォルトのファクトリを使用している場合)、スレッドを中止してもタスクに影響はありません。タスクの中止については、msdnの Task Cancellation を参照してください。

2
Oliver Hanappi

タスクには キャンセルトークン によるキャンセルのファーストクラスサポートがあります。キャンセルトークンを使用してタスクを作成し、これらを明示的に介してタスクをキャンセルします。

2
Tim Lloyd

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