web-dev-qa-db-ja.com

C#タスクファクトリタイムアウト

スレッドで長いプロセス操作を実行し、結果を関数に返すことで続行する必要があります。これが私のコードです:

Task<ProductEventArgs>.Factory.StartNew(() =>
    {
        try
        {
             // long operation which return new ProductEventArgs with a list of product

        }
        catch (Exception e)
        {
            return new ProductEventArgs() { E = e };
        }

    }).ContinueWith((x) => handleResult(x.Result), TaskScheduler.FromCurrentSynchronizationContext());

問題は、実際にはタイムアウトがないことです。私はこのようなものを返すためにタイマーを置きたいです:

   new ProductEventArgs() { E = new Exception("timeout") }; 

タイムアウトに達した場合。 await/asyncは使用できません。どうもありがとう !

11
Rototo

このコードは、ここで表現したことを実行します。

var timeout = TimeSpan.FromSeconds(5);

var actualTask = new Task<ProductEventArgs>(() =>
{
    var longRunningTask = new Task<ProductEventArgs>(() =>
    {
        try
        {
            Thread.Sleep(TimeSpan.FromSeconds(10)); // simulates the long running computation
            return new ProductEventArgs();
        }
        catch (Exception e)
        {
            return new ProductEventArgs() { E = e };
        }
    }, TaskCreationOptions.LongRunning);

    longRunningTask.Start();

    if (longRunningTask.Wait(timeout)) return longRunningTask.Result;

    return new ProductEventArgs() { E = new Exception("timed out") };
});

actualTask.Start();

actualTask.Wait();

Console.WriteLine("{0}", actualTask.Result.E); // handling E

ご覧のとおり、longRunningTaskTaskCreationOptions.LongRunningオプションで作成されます。そうすれば、実行専用のThreadがあり、そこからスレッドを長時間占有することによってThreadPoolの通常の動作を妨げることはありません。これは、UIなどの他のものに必要になります。 。 これは長時間実行されるタスクにとって重要です

注:その後、actualTaskContinueWithで処理できますが、ここで本質を表現したいと思います。

5

CancellationTokensを使用する必要があります:

var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10));
var token = cts.Token;
Task<ProductEventArgs>.Factory.StartNew(() =>
{
    try
    {
        // occasionally, execute this line:
        token.ThrowIfCancellationRequested();
    }
    catch (OperationCanceledException)
    {
        return new ProductEventArgs() { E = new Exception("timeout") };
    }
    catch (Exception e)
    {
        return new ProductEventArgs() { E = e };
    }

}).ContinueWith((x) => handleResult(x.Result), TaskScheduler.FromCurrentSynchronizationContext());
17
Stephen Cleary

StartNewメソッドに返されたタスクオブジェクトを使用してから、ユーザーのWaitメソッドを使用してタイムアウトを決定できます。

Task<ProductEventArgs> task = Task<ProductEventArgs>.Factory.StartNew(() => {...});
if (!Task.Wait(new TimeSpan(0,0,1,0)) // wait for 1 minute
{
   // throw exception or something else if timeout
}
3
Uzzy

Task.Delay(timeout)タスクを並行して実行し、最初に完了したタスクを確認できます(この場合、Task.WhenAny()は非常に便利です)。

public void FetchProduct(TimeSpan timeout)
{
    var fetchTask = Task<ProductEventArgs>.Factory.StartNew(
        () =>
        {
            try
            {
                // long operation which return new ProductEventArgs with a list of product
            }
            catch(Exception e)
            {
                return new ProductEventArgs() { E = e };
            }
        });
    Task<ProductEventArgs> resultTask;
    if(timeout != Timeout.InfiniteTimeSpan)
    {
        var timeoutTask = Task.Delay(timeout);
        resultTask = Task.WhenAny(resultTask, timeoutTask).ContinueWith<ProductEventArgs>(
            t =>
            {
                // completed task is the result of WhenAny
                if(t.Result == fetchTask)
                {
                    return fetchTask.Result;
                }
                else
                {
                    return new ProductEventArgs() { E = new TimeoutException() };
                }
            });
    }
    else
    {
        resultTask = fetchTask;
    }
    resultTask.ContinueWith(x => handleResult(x.Result), TaskScheduler.FromCurrentSynchronizationContext());
}

このソリューションにはキャンセルロジックがなく、長時間実行されているタスクはタイムアウトしても実行され続けることに注意してください。

1
max

メインタスク(代理)内で別のタスクを開始するだけです。

Task.Factory.StartNew(() => 
        {
            // returns a string result
            var tsk = new Task<string>(() => { return VeryImportantThingsToDo(); });
            try
            {
                tsk.Start();
                if (!tsk.Wait(5000))
                    throw new TimeoutException();
                return tsk.Result;
            }
            catch (TimeoutException)
            {
                // Jabba Dabba Doooooooohhhhhh
            }

            return "<unknown>";
        }).ContinueWith((o) => string result = o.Result));
0