C# 任务工厂超时

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/16605223/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-10 01:26:11  来源:igfitidea点击:

C# task factory timeout

c#timeouttaskfactory

提问by Rototo

I have to execute a long process operation in a thread and continue by returning the result to a function. Here is my code :

我必须在一个线程中执行一个很长的进程操作,然后将结果返回给一个函数来继续。这是我的代码:

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

The problem is actually I don't have a timeout. I want to put a timer in order to return something like this :

问题实际上是我没有超时。我想放置一个计时器以返回如下内容:

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

if the timeout is reached. Can't use await/async. Thanks a lot !

如果超时。不能使用等待/异步。非常感谢 !

采纳答案by Kaveh Shahbazian

This code does what you have expressed here:

此代码执行您在此处表达的内容:

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

As you see longRunningTaskis created with TaskCreationOptions.LongRunningoption. That way it will have a dedicated Threadfor it's execution and does not interfere with normal behavior of ThreadPoolby occupying a thread from there for too long - which will be needed for other thing like i.e. UI. That's important for long running tasks.

如您所见,longRunningTask是使用TaskCreationOptions.LongRunning选项创建的。这样它将有一个专用Thread于它的执行并且不会ThreadPool通过从那里占用线程太长时间来干扰正常行为- 这将需要其他东西,例如 UI。这对于长时间运行的任务很重要

Note: You could then handle actualTaskwith ContinueWithbut I wanted to express the essence here.

注意:然后你可以处理actualTaskContinueWith但我想在这里表达本质。

回答by max

You can run a Task.Delay(timeout)task in parallel and check what task was first to complete (Task.WhenAny()is very handy in this case):

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

Note that this solution doesn't have any cancellation logic, and your long running task will be still running even if it times out.

请注意,此解决方案没有任何取消逻辑,即使超时,您的长时间运行的任务仍将继续运行。

回答by Stephen Cleary

You should use CancellationTokens:

您应该使用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());

回答by Uzzy

You may use returned task object for StartNew method and then user Wait method to determine timeout.

您可以将返回的任务对象用于 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
}

回答by Martin.Martinsson

Just start another task within the main task (surrogate):

只需在主任务(代理)中启动另一个任务:

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