C# 异步等待任务<;T>;以超时完成
我想等待a完成一些特殊规则: 如果X毫秒后还没有完成,我想向用户显示一条消息。 如果在Y毫秒后还没有完成,我想自动完成 Ican可以使用异步等待任务完成(即计划任务完成时执行的操作),但不允许指定超时。 我可以使用来同步等待任务完成并超时,但这会阻塞我的线程。C# 异步等待任务<;T>;以超时完成,c#,.net,task-parallel-library,C#,.net,Task Parallel Library,我想等待a完成一些特殊规则: 如果X毫秒后还没有完成,我想向用户显示一条消息。 如果在Y毫秒后还没有完成,我想自动完成 Ican可以使用异步等待任务完成(即计划任务完成时执行的操作),但不允许指定超时。 我可以使用来同步等待任务完成并超时,但这会阻塞我的线程。 如何在超时的情况下异步等待任务完成?这样的事情怎么样 const int x = 3000; const int y = 1000; static void Main(string[] args) {
如何在超时的情况下异步等待任务完成?这样的事情怎么样
const int x = 3000;
const int y = 1000;
static void Main(string[] args)
{
// Your scheduler
TaskScheduler scheduler = TaskScheduler.Default;
Task nonblockingTask = new Task(() =>
{
CancellationTokenSource source = new CancellationTokenSource();
Task t1 = new Task(() =>
{
while (true)
{
// Do something
if (source.IsCancellationRequested)
break;
}
}, source.Token);
t1.Start(scheduler);
// Wait for task 1
bool firstTimeout = t1.Wait(x);
if (!firstTimeout)
{
// If it hasn't finished at first timeout display message
Console.WriteLine("Message to user: the operation hasn't completed yet.");
bool secondTimeout = t1.Wait(y);
if (!secondTimeout)
{
source.Cancel();
Console.WriteLine("Operation stopped!");
}
}
});
nonblockingTask.Start();
Console.WriteLine("Do whatever you want...");
Console.ReadLine();
}
您可以使用Task.Wait选项,而不使用其他任务阻塞主线程。您可以使用
Task.WaitAny
等待多个任务中的第一个
您可以创建两个附加任务(在指定的超时后完成),然后使用WaitAny
等待先完成的任务。如果首先完成的任务是你的“工作”任务,那么你就完成了。如果首先完成的任务是超时任务,则您可以对超时做出反应(例如,请求取消)。使用来处理消息和自动取消。任务完成后,调用计时器上的Dispose,使其永远不会触发。这是一个例子;将taskDelay更改为500、1500或2500以查看不同的情况:
using System;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
class Program
{
private static Task CreateTaskWithTimeout(
int xDelay, int yDelay, int taskDelay)
{
var cts = new CancellationTokenSource();
var token = cts.Token;
var task = Task.Factory.StartNew(() =>
{
// Do some work, but fail if cancellation was requested
token.WaitHandle.WaitOne(taskDelay);
token.ThrowIfCancellationRequested();
Console.WriteLine("Task complete");
});
var messageTimer = new Timer(state =>
{
// Display message at first timeout
Console.WriteLine("X milliseconds elapsed");
}, null, xDelay, -1);
var cancelTimer = new Timer(state =>
{
// Display message and cancel task at second timeout
Console.WriteLine("Y milliseconds elapsed");
cts.Cancel();
}
, null, yDelay, -1);
task.ContinueWith(t =>
{
// Dispose the timers when the task completes
// This will prevent the message from being displayed
// if the task completes before the timeout
messageTimer.Dispose();
cancelTimer.Dispose();
});
return task;
}
static void Main(string[] args)
{
var task = CreateTaskWithTimeout(1000, 2000, 2500);
// The task has been started and will display a message after
// one timeout and then cancel itself after the second
// You can add continuations to the task
// or wait for the result as needed
try
{
task.Wait();
Console.WriteLine("Done waiting for task");
}
catch (AggregateException ex)
{
Console.WriteLine("Error waiting for task:");
foreach (var e in ex.InnerExceptions)
{
Console.WriteLine(e);
}
}
}
}
}
此外,还提供了一个TaskEx.Delay方法,可以将计时器包装到任务中。这可以让您更好地控制计时器启动时的后续操作,如设置TaskScheduler
private static Task CreateTaskWithTimeout(
int xDelay, int yDelay, int taskDelay)
{
var cts = new CancellationTokenSource();
var token = cts.Token;
var task = Task.Factory.StartNew(() =>
{
// Do some work, but fail if cancellation was requested
token.WaitHandle.WaitOne(taskDelay);
token.ThrowIfCancellationRequested();
Console.WriteLine("Task complete");
});
var timerCts = new CancellationTokenSource();
var messageTask = TaskEx.Delay(xDelay, timerCts.Token);
messageTask.ContinueWith(t =>
{
// Display message at first timeout
Console.WriteLine("X milliseconds elapsed");
}, TaskContinuationOptions.OnlyOnRanToCompletion);
var cancelTask = TaskEx.Delay(yDelay, timerCts.Token);
cancelTask.ContinueWith(t =>
{
// Display message and cancel task at second timeout
Console.WriteLine("Y milliseconds elapsed");
cts.Cancel();
}, TaskContinuationOptions.OnlyOnRanToCompletion);
task.ContinueWith(t =>
{
timerCts.Cancel();
});
return task;
}
这个怎么样:
int timeout = 1000;
var task = SomeOperationAsync();
if (await Task.WhenAny(task, Task.Delay(timeout)) == task) {
// task completed within timeout
} else {
// timeout logic
}
这是我的
添加:应对我的回答发表评论的要求,这里有一个扩展的解决方案,包括取消处理。请注意,将取消传递给任务和计时器意味着在代码中有多种方式可以经历取消,您应该确保测试并确信您正确地处理了所有这些方式。不要让你的计算机在运行时做正确的事情,而去碰各种组合的机会
int timeout = 1000;
var task = SomeOperationAsync(cancellationToken);
if (await Task.WhenAny(task, Task.Delay(timeout, cancellationToken)) == task)
{
// Task completed within timeout.
// Consider that the task may have faulted or been canceled.
// We re-await the task so that any exceptions/cancellation is rethrown.
await task;
}
else
{
// timeout/cancellation logic
}
下面是一个扩展方法版本,它包含了在原始任务完成时取消超时,正如Andrew Arnott在对的注释中所建议的那样
公共静态异步任务TimeoutAfter(此任务,TimeSpan超时){
使用(var timeoutCancellationTokenSource=new CancellationTokenSource()){
var completedTask=wait Task.WhenAny(Task,Task.Delay(timeout,timeoutCancellationTokenSource.Token));
if(completedTask==任务){
timeoutCancellationTokenSource.Cancel();
return wait task;//传播异常非常重要
}否则{
抛出新的TimeoutException(“操作已超时”);
}
}
}
以下是一个基于最热门答案的完整示例,即:
int timeout = 1000;
var task = SomeOperationAsync();
if (await Task.WhenAny(task, Task.Delay(timeout)) == task) {
// task completed within timeout
} else {
// timeout logic
}
这个答案中实现的主要优点是添加了泛型,因此函数(或任务)可以返回值。这意味着任何现有函数都可以包装在超时函数中,例如:
之前:
之后:
此代码需要.NET 4.5
using System;
using System.Threading;
using System.Threading.Tasks;
namespace TaskTimeout
{
public static class Program
{
/// <summary>
/// Demo of how to wrap any function in a timeout.
/// </summary>
private static void Main(string[] args)
{
// Version without timeout.
int a = MyFunc();
Console.Write("Result: {0}\n", a);
// Version with timeout.
int b = TimeoutAfter(() => { return MyFunc(); },TimeSpan.FromSeconds(1));
Console.Write("Result: {0}\n", b);
// Version with timeout (short version that uses method groups).
int c = TimeoutAfter(MyFunc, TimeSpan.FromSeconds(1));
Console.Write("Result: {0}\n", c);
// Version that lets you see what happens when a timeout occurs.
try
{
int d = TimeoutAfter(
() =>
{
Thread.Sleep(TimeSpan.FromSeconds(123));
return 42;
},
TimeSpan.FromSeconds(1));
Console.Write("Result: {0}\n", d);
}
catch (TimeoutException e)
{
Console.Write("Exception: {0}\n", e.Message);
}
// Version that works on tasks.
var task = Task.Run(() =>
{
Thread.Sleep(TimeSpan.FromSeconds(1));
return 42;
});
// To use async/await, add "await" and remove "GetAwaiter().GetResult()".
var result = task.TimeoutAfterAsync(TimeSpan.FromSeconds(2)).
GetAwaiter().GetResult();
Console.Write("Result: {0}\n", result);
Console.Write("[any key to exit]");
Console.ReadKey();
}
public static int MyFunc()
{
return 42;
}
public static TResult TimeoutAfter<TResult>(
this Func<TResult> func, TimeSpan timeout)
{
var task = Task.Run(func);
return TimeoutAfterAsync(task, timeout).GetAwaiter().GetResult();
}
private static async Task<TResult> TimeoutAfterAsync<TResult>(
this Task<TResult> task, TimeSpan timeout)
{
var result = await Task.WhenAny(task, Task.Delay(timeout));
if (result == task)
{
// Task completed within timeout.
return task.GetAwaiter().GetResult();
}
else
{
// Task timed out.
throw new TimeoutException();
}
}
}
}
使用系统;
使用系统线程;
使用System.Threading.Tasks;
命名空间任务超时
{
公共静态类程序
{
///
///演示如何在超时时间内包装任何函数。
///
私有静态void Main(字符串[]args)
{
//没有超时的版本。
int a=MyFunc();
Write(“结果:{0}\n”,a);
//具有超时的版本。
int b=TimeoutAfter(()=>{return MyFunc();},TimeSpan.FromSeconds(1));
Write(“结果:{0}\n”,b);
//具有超时的版本(使用方法组的短版本)。
int c=TimeoutAfter(MyFunc,TimeSpan.FromSeconds(1));
Write(“结果:{0}\n”,c);
//允许您查看发生超时时发生的情况的版本。
尝试
{
int d=超时之后(
() =>
{
线程睡眠(TimeSpan.FromSeconds(123));
返回42;
},
时间跨度从秒(1)开始;
Write(“结果:{0}\n”,d);
}
捕获(超时异常e)
{
Write(“异常:{0}\n”,e.Message);
}
//用于任务的版本。
var task=task.Run(()=>
{
线程睡眠(TimeSpan.FromSeconds(1));
返回42;
});
//要使用async/await,请添加“await”并删除“GetAwaiter().GetResult()”。
var result=task.TimeoutAfterAsync(TimeSpan.FromSeconds(2))。
GetAwaiter().GetResult();
Write(“结果:{0}\n”,Result);
控制台。写入(“[任何退出键]”);
Console.ReadKey();
}
公共静态int MyFunc()
{
返回42;
}
公共静态TResult TimeoutAfter(
此函数(函数,TimeSpan超时)
{
var task=task.Run(func);
返回TimeoutAfterAsync(任务,超时).GetAwaiter().GetResult();
}
专用静态异步任务TimeoutAfterAsync(
此任务(任务,TimeSpan超时)
{
var结果=等待任务.WhenAny(任务,任务.延迟(超时));
如果(结果==任务)
{
//任务在超时时间内完成。
返回task.GetAwaiter().GetResult();
}
其他的
{
//任务超时。
抛出新的TimeoutException();
}
}
}
}
注意事项
给出这个答案后,它是g
int x = MyFunc();
// Throws a TimeoutException if MyFunc takes more than 1 second
int x = TimeoutAfter(MyFunc, TimeSpan.FromSeconds(1));
using System;
using System.Threading;
using System.Threading.Tasks;
namespace TaskTimeout
{
public static class Program
{
/// <summary>
/// Demo of how to wrap any function in a timeout.
/// </summary>
private static void Main(string[] args)
{
// Version without timeout.
int a = MyFunc();
Console.Write("Result: {0}\n", a);
// Version with timeout.
int b = TimeoutAfter(() => { return MyFunc(); },TimeSpan.FromSeconds(1));
Console.Write("Result: {0}\n", b);
// Version with timeout (short version that uses method groups).
int c = TimeoutAfter(MyFunc, TimeSpan.FromSeconds(1));
Console.Write("Result: {0}\n", c);
// Version that lets you see what happens when a timeout occurs.
try
{
int d = TimeoutAfter(
() =>
{
Thread.Sleep(TimeSpan.FromSeconds(123));
return 42;
},
TimeSpan.FromSeconds(1));
Console.Write("Result: {0}\n", d);
}
catch (TimeoutException e)
{
Console.Write("Exception: {0}\n", e.Message);
}
// Version that works on tasks.
var task = Task.Run(() =>
{
Thread.Sleep(TimeSpan.FromSeconds(1));
return 42;
});
// To use async/await, add "await" and remove "GetAwaiter().GetResult()".
var result = task.TimeoutAfterAsync(TimeSpan.FromSeconds(2)).
GetAwaiter().GetResult();
Console.Write("Result: {0}\n", result);
Console.Write("[any key to exit]");
Console.ReadKey();
}
public static int MyFunc()
{
return 42;
}
public static TResult TimeoutAfter<TResult>(
this Func<TResult> func, TimeSpan timeout)
{
var task = Task.Run(func);
return TimeoutAfterAsync(task, timeout).GetAwaiter().GetResult();
}
private static async Task<TResult> TimeoutAfterAsync<TResult>(
this Task<TResult> task, TimeSpan timeout)
{
var result = await Task.WhenAny(task, Task.Delay(timeout));
if (result == task)
{
// Task completed within timeout.
return task.GetAwaiter().GetResult();
}
else
{
// Task timed out.
throw new TimeoutException();
}
}
}
}
public static Task TimeoutAfter(this Task task, TimeSpan timeout, IScheduler scheduler)
{
return task.ToObservable().Timeout(timeout, scheduler).ToTask();
}
TestScheduler scheduler = new TestScheduler();
Task task = Task.Run(() =>
{
int i = 0;
while (i < 5)
{
Console.WriteLine(i);
i++;
Thread.Sleep(1000);
}
})
.TimeoutAfter(TimeSpan.FromSeconds(5), scheduler)
.ContinueWith(t => { }, TaskContinuationOptions.OnlyOnFaulted);
scheduler.AdvanceBy(TimeSpan.FromSeconds(6).Ticks);
using System.Threading.Tasks;
using System.Reactive.Subjects;
using System.Reactive.Linq;
using System.Reactive.Threading.Tasks;
using Microsoft.Reactive.Testing;
using System.Threading;
using System.Reactive.Concurrency;
public static Task<T> TimeoutAfter<T>(this Task<T> task, TimeSpan timeout, Scheduler scheduler = null)
{
return scheduler is null
? task.ToObservable().Timeout(timeout).ToTask()
: task.ToObservable().Timeout(timeout, scheduler).ToTask();
}
TimeSpan timeout = TimeSpan.FromSeconds(10);
using (var cts = new CancellationTokenSource(timeout))
{
await myTask.WaitAsync(cts.Token);
}
public static async Task<bool> BeforeTimeout(this Task task, int millisecondsTimeout)
{
if (task.IsCompleted) return true;
if (millisecondsTimeout == 0) return false;
if (millisecondsTimeout == Timeout.Infinite)
{
await Task.WhenAll(task);
return true;
}
var tcs = new TaskCompletionSource<object>();
using (var timer = new Timer(state => ((TaskCompletionSource<object>)state).TrySetCanceled(), tcs,
millisecondsTimeout, Timeout.Infinite))
{
return await Task.WhenAny(task, tcs.Task) == task;
}
}
var receivingTask = conn.ReceiveAsync(ct);
while (!await receivingTask.BeforeTimeout(keepAliveMilliseconds))
{
// Send keep-alive
}
// Read and do something with data
var data = await receivingTask;
public static async Task<bool> TimedOutAsync(this Task task, int timeoutMilliseconds)
{
if (timeoutMilliseconds < 0 || (timeoutMilliseconds > 0 && timeoutMilliseconds < 100)) { throw new ArgumentOutOfRangeException(); }
if (timeoutMilliseconds == 0) {
return !task.IsCompleted; // timed out if not completed
}
var cts = new CancellationTokenSource();
if (await Task.WhenAny( task, Task.Delay(timeoutMilliseconds, cts.Token)) == task) {
cts.Cancel(); // task completed, get rid of timer
await task; // test for exceptions or task cancellation
return false; // did not timeout
} else {
return true; // did timeout
}
}
public static async Task<T> CancelAfterAsync<T>( this Func<CancellationToken,Task<T>> actionAsync, int timeoutMilliseconds)
{
if (timeoutMilliseconds < 0 || (timeoutMilliseconds > 0 && timeoutMilliseconds < 100)) { throw new ArgumentOutOfRangeException(); }
var taskCts = new CancellationTokenSource();
var timerCts = new CancellationTokenSource();
Task<T> task = actionAsync(taskCts.Token);
if (await Task.WhenAny(task, Task.Delay(timeoutMilliseconds, timerCts.Token)) == task) {
timerCts.Cancel(); // task completed, get rid of timer
} else {
taskCts.Cancel(); // timer completed, get rid of task
}
return await task; // test for exceptions or task cancellation
}
public static async Task<T> CancelAfterAsync<T>(this Task<T> task, int timeoutMilliseconds, CancellationTokenSource taskCts)
{
if (timeoutMilliseconds < 0 || (timeoutMilliseconds > 0 && timeoutMilliseconds < 100)) { throw new ArgumentOutOfRangeException(); }
var timerCts = new CancellationTokenSource();
if (await Task.WhenAny(task, Task.Delay(timeoutMilliseconds, timerCts.Token)) == task) {
timerCts.Cancel(); // task completed, get rid of timer
} else {
taskCts.Cancel(); // timer completed, get rid of task
}
return await task; // test for exceptions or task cancellation
}
async Task<int> CallAsync(CancellationToken cancellationToken)
{
var timeout = TimeSpan.FromMinutes(1);
int result = await CancelAfterAsync(ct => InnerCallAsync(ct), timeout,
cancellationToken);
return result;
}
async Task<int> InnerCallAsync(CancellationToken cancellationToken)
{
return 42;
}
public static async Task<bool> TryWithTimeoutAfter<TResult>(this Task<TResult> task,
TimeSpan timeout, Action<TResult> successor)
{
using var timeoutCancellationTokenSource = new CancellationTokenSource();
var completedTask = await Task.WhenAny(task, Task.Delay(timeout, timeoutCancellationTokenSource.Token))
.ConfigureAwait(continueOnCapturedContext: false);
if (completedTask == task)
{
timeoutCancellationTokenSource.Cancel();
// propagate exception rather than AggregateException, if calling task.Result.
var result = await task.ConfigureAwait(continueOnCapturedContext: false);
successor(result);
return true;
}
else return false;
}
async Task Example(Task<string> task)
{
string result = null;
if (await task.TryWithTimeoutAfter(TimeSpan.FromSeconds(1), r => result = r))
{
Console.WriteLine(result);
}
}
((CancellationTokenSource)cancellationToken.GetType().GetField("m_source",
System.Reflection.BindingFlags.NonPublic |
System.Reflection.BindingFlags.Instance
).GetValue(cancellationToken)).Cancel();