我必须在一个线程中执行一个长进程操作,并继续将结果返回给一个函数。这是我的代码:
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。 非常感谢!
答案 0 :(得分:14)
您应该使用CancellationToken
s:
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());
答案 1 :(得分:5)
此代码执行您在此处表达的内容:
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
如您所见longRunningTask
是使用TaskCreationOptions.LongRunning
选项创建的。这样它就会有一个专用的Thread
用于它的执行,并且不会通过从那里占用一个线程太长时间来干扰ThreadPool
的正常行为 - 这将是其他事情所需要的即UI。 对于长时间运行的任务非常重要。
注意:然后您可以使用actualTask
处理ContinueWith
,但我想在此处表达精华。
答案 2 :(得分:3)
您可以将返回的任务对象用于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 :(得分:1)
您可以并行运行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());
}
请注意,此解决方案没有任何取消逻辑,即使超时,您的长时间运行任务仍将继续运行。
答案 4 :(得分:0)
在主要任务(代理人)中启动另一项任务:
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));