14

長時間実行されているタスク (.net 4) を停止するにはどうすればよいですか?

TPL を実装して を使用してみましたCancellationTokenSourceが、私のシナリオではうまくいかないようです。私が見たすべての例は、タスクがキャンセルされたかどうかを確認できるように while ループで作業を行っていることを前提としていますが、私は時間がかかる単一の操作しかありません。作業が完了しない可能性があると想定する必要があるため、作業が完了するのが待ちきれません。これが私が試したコードです:

bool? result = null;

var cs = new CancellationTokenSource();
var ct = cs.Token;

var doWorkTask = new Task(() =>
{
    Console.WriteLine("start dowork task");

    result = Work.LongRunning();
 }, ct);

doWorkTask.Start();

Task.WaitAny(new Task[] { doWorkTask }, timetowait);

if (doWorkTask.IsCompleted)
{
    Console.WriteLine("dowork task completed");

    doWorkTask.Dispose();
}
else
{
    Console.WriteLine("dowork task has timedout");

    cs.Cancel();

    throw new TimeoutException("Timeout hit.");
}

コードは機能しますが、タイムアウトが発生し、実行中の作業が「管理されていないコード」、つまりリソー​​スにアクセスする場合、タスクは決して破棄されません。とは言ってIsCancelledRequestedも使えないWork.LongRunning()ので使えませんThrowIfCancellationRequested

私は試したのと同じように他のアイデアにもオープンですBackgroundWorkerが、それも合わないようです.

新しい例:

var service = new System.ServiceProcess.ServiceController(ServiceName, ServerName);

var serviceTask = Task.Factory.StartNew(() =>
{
    result = (service.Status == ServiceControllerStatus.Running
         || service.Status == ServiceControllerStatus.StartPending);
}, cs.Token);

serviceTask.Wait(2000, cs.Token);

if (!serviceTask.IsCompleted)
{
    cs.Cancel();
}
4

2 に答える 2

4

上記のオプション 1 の例を次に示します (つまり、キャンセルを通知せずにタスクを強制終了するだけです)。

class Program
    {
        private static void Main(string[] args)
        {
            Test test = new Test();
            test.Run();

            Console.WriteLine("Type c to cancel");
            if (Console.ReadLine().StartsWith("c"))
            {
                Console.WriteLine("cancellation requested");
                test.CancellationTokenSource.Cancel();
            }

            Console.ReadLine();
        }
    }

    public class Test
    {
        private void DoSomething()
        {
            Console.WriteLine("DoSomething runs for 30 seconds ");
            Thread.Sleep(new TimeSpan(0, 0, 0, 30));
            Console.WriteLine("woke up now ");
        }

        public CancellationTokenSource CancellationTokenSource = new CancellationTokenSource();

        public void Run()
        {
                var generateReportsTask = Task.Factory.StartNew(() =>
                {
                    CancellationTokenSource.Token.ThrowIfCancellationRequested();
                    Task doSomething = new Task(DoSomething, CancellationTokenSource.Token);
                    doSomething.Start();

                    doSomething.Wait(CancellationTokenSource.Token);
                }, CancellationTokenSource.Token);

                generateReportsTask.ContinueWith(
                    (t) =>
                    {
                        if (t.Exception != null)
                            Console.WriteLine("Exceptions reported :\n " + t.Exception);

                        if (t.Status == TaskStatus.RanToCompletion)
                            Console.WriteLine("Completed report generation task");
                        if (t.Status == TaskStatus.Faulted)
                            Console.WriteLine("Completed reported generation with unhandeled exceptions");
                        if(t.Status == TaskStatus.Canceled)
                            Console.WriteLine("The Task Has been cancelled");
                    });

        }
    }
于 2015-02-05T17:26:50.147 に答える
2

Task Parallel Library は、CPU を集中的に使用する作業用に設計されています。CPU を集中的に使用する作業はしばらく見てから行います。Work.LongRunning() が CPU を集中的に使用する場合、キャンセル トークンを内部に渡してキャンセルできるはずです。CPU を集中的に使用しない場合は、最終的なコールバックで結果を破棄するだけでよく、待機しているだけなので実際の作業を停止する必要はありません。

ところで、(データベース呼び出しなどを)待っている場合は、おそらく下部のどこかに非同期メソッドがあります。Begin/End パターンを表面化し、それを Task にラップできます。TPL TaskFactory.FromAsync とブロッキング メソッドを使用したタスクIO待機は OS によって処理される特別な方法で行われるため、この方法で汎用スレッドを占有することを回避できます。

于 2012-05-02T07:24:43.310 に答える