在 .Net 4.0 中取消任务延迟
Canceling Task Delay in .Net 4.0
我目前正在尝试在必须以 .Net 4.0 为目标的程序中实现 .Net 4.5 的 Task.Delay()
方法的替代方法。我在 this blog.
找到了以下代码
/* You can write Task-based asynchronous methods by utilizing a TaskCompletionSource.
A TaskCompletionSource gives you a 'slave' Task that you can manually signal.
Calling SetResult() signals the task as complete, and any continuations kick off. */
void Main()
{
for (int i = 0; i < 10000; i++)
{
Task task = Delay (2000);
task.ContinueWith (_ => "Done".Dump());
}
}
Task Delay (int milliseconds) // Asynchronous NON-BLOCKING method
{
var tcs = new TaskCompletionSource<object>();
new Timer (_ => tcs.SetResult (null)).Change (milliseconds, -1);
return tcs.Task;
}
Tasks
对我来说相当陌生。 System.Threading.Timer
和 TaskCompletionSource
对我来说是全新的(截至今天),我对它们有些吃力。除此之外,我想知道如何向这段代码添加 CancellationToken
功能。我假设我可以像这样向 Delay()
方法添加一个参数:
Task Delay (int milliseconds, CancellationToken token) // Asynchronous NON-BLOCKING method
{
var tcs = new TaskCompletionSource<object>();
new Timer (_ => tcs.SetResult (null)).Change (milliseconds, -1);
return tcs.Task;
}
...但是,我应该把检查令牌和退出方法的逻辑放在哪里?回调中的某个地方?这可能吗?
喜欢this:
token.Register(() => tcs.TrySetCancelled());
我已尝试尽可能少地更改您的代码,但这里有一个工作示例,其行为方式与 Task.Delay 相同。
重要的是要注意我使用 TrySetCanceled
和 TrySetResult
因为定时器可以在任务取消后完成。理想情况下,您想要停止计时器。
另请注意,取消的任务将抛出 TaskCanceledException
static void Main(string[] args)
{
// A cancellation source that will cancel itself after 1 second
var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(1));
try
{
// This will only wait 1 second because as it will be cancelled.
Task t = Delay(5000, cancellationTokenSource.Token);
t.Wait();
Console.WriteLine("The task completed");
}
catch (AggregateException exception)
{
// Expecting a TaskCanceledException
foreach (Exception ex in exception.InnerExceptions)
Console.WriteLine("Exception: {0}", ex.Message);
}
Console.WriteLine("Done");
Console.ReadLine();
}
private static Task Delay(int milliseconds, CancellationToken token)
{
var tcs = new TaskCompletionSource<object>();
token.Register(() => tcs.TrySetCanceled());
Timer timer = new Timer(_ => tcs.TrySetResult(null));
timer.Change(milliseconds, -1);
return tcs.Task;
}
进一步了解您的问题。如果您需要 Task.Delay 并且您的目标是 .NET 4.0,那么您应该使用来自 http://www.nuget.org/packages/Microsoft.Bcl.Async/ 的 Microsoft Async nuget 包,它包含方法 TaskEx.Delay
这是一个防止垃圾收集器处理计时器的版本
public static Task Delay(int milliseconds, CancellationToken token)
{
var tcs = new TaskCompletionSource<object>();
var timer = new OneShotTimer((t) => {
using ((OneShotTimer)t)
tcs.SetResult(null);
});
token.Register(() => {
if (timer.TryCancel())
{
using (timer)
tcs.SetCanceled();
}
});
timer.Start(milliseconds);
return tcs.Task;
}
public class OneShotTimer : IDisposable
{
private readonly object sync = new object();
private readonly TimerCallback oneShotCallback;
private readonly Timer timer;
private bool isActive;
public OneShotTimer(TimerCallback oneShotCallback, int dueTime = Timeout.Infinite)
{
this.oneShotCallback = oneShotCallback;
this.isActive = dueTime != Timeout.Infinite;
this.timer = new Timer(callback, this, dueTime, Timeout.Infinite);
}
public void Dispose()
{
timer.Dispose();
}
public void Start(int dueTime)
{
if (!tryChange(true, dueTime))
throw new InvalidOperationException("The timer has already been started");
}
public bool TryCancel()
{
return tryChange(false, Timeout.Infinite);
}
public bool tryChange(bool targetIsActive, int dueTime)
{
bool result = false;
lock (sync)
{
if (isActive != targetIsActive)
{
result = true;
isActive = targetIsActive;
timer.Change(dueTime, Timeout.Infinite);
}
}
return result;
}
private static void callback(object state)
{
var oneShotTimer = (OneShotTimer)state;
if (oneShotTimer.TryCancel())
oneShotTimer.oneShotCallback(oneShotTimer);
}
}
我目前正在尝试在必须以 .Net 4.0 为目标的程序中实现 .Net 4.5 的 Task.Delay()
方法的替代方法。我在 this blog.
/* You can write Task-based asynchronous methods by utilizing a TaskCompletionSource.
A TaskCompletionSource gives you a 'slave' Task that you can manually signal.
Calling SetResult() signals the task as complete, and any continuations kick off. */
void Main()
{
for (int i = 0; i < 10000; i++)
{
Task task = Delay (2000);
task.ContinueWith (_ => "Done".Dump());
}
}
Task Delay (int milliseconds) // Asynchronous NON-BLOCKING method
{
var tcs = new TaskCompletionSource<object>();
new Timer (_ => tcs.SetResult (null)).Change (milliseconds, -1);
return tcs.Task;
}
Tasks
对我来说相当陌生。 System.Threading.Timer
和 TaskCompletionSource
对我来说是全新的(截至今天),我对它们有些吃力。除此之外,我想知道如何向这段代码添加 CancellationToken
功能。我假设我可以像这样向 Delay()
方法添加一个参数:
Task Delay (int milliseconds, CancellationToken token) // Asynchronous NON-BLOCKING method
{
var tcs = new TaskCompletionSource<object>();
new Timer (_ => tcs.SetResult (null)).Change (milliseconds, -1);
return tcs.Task;
}
...但是,我应该把检查令牌和退出方法的逻辑放在哪里?回调中的某个地方?这可能吗?
喜欢this:
token.Register(() => tcs.TrySetCancelled());
我已尝试尽可能少地更改您的代码,但这里有一个工作示例,其行为方式与 Task.Delay 相同。
重要的是要注意我使用 TrySetCanceled
和 TrySetResult
因为定时器可以在任务取消后完成。理想情况下,您想要停止计时器。
另请注意,取消的任务将抛出 TaskCanceledException
static void Main(string[] args)
{
// A cancellation source that will cancel itself after 1 second
var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(1));
try
{
// This will only wait 1 second because as it will be cancelled.
Task t = Delay(5000, cancellationTokenSource.Token);
t.Wait();
Console.WriteLine("The task completed");
}
catch (AggregateException exception)
{
// Expecting a TaskCanceledException
foreach (Exception ex in exception.InnerExceptions)
Console.WriteLine("Exception: {0}", ex.Message);
}
Console.WriteLine("Done");
Console.ReadLine();
}
private static Task Delay(int milliseconds, CancellationToken token)
{
var tcs = new TaskCompletionSource<object>();
token.Register(() => tcs.TrySetCanceled());
Timer timer = new Timer(_ => tcs.TrySetResult(null));
timer.Change(milliseconds, -1);
return tcs.Task;
}
进一步了解您的问题。如果您需要 Task.Delay 并且您的目标是 .NET 4.0,那么您应该使用来自 http://www.nuget.org/packages/Microsoft.Bcl.Async/ 的 Microsoft Async nuget 包,它包含方法 TaskEx.Delay
这是一个防止垃圾收集器处理计时器的版本
public static Task Delay(int milliseconds, CancellationToken token)
{
var tcs = new TaskCompletionSource<object>();
var timer = new OneShotTimer((t) => {
using ((OneShotTimer)t)
tcs.SetResult(null);
});
token.Register(() => {
if (timer.TryCancel())
{
using (timer)
tcs.SetCanceled();
}
});
timer.Start(milliseconds);
return tcs.Task;
}
public class OneShotTimer : IDisposable
{
private readonly object sync = new object();
private readonly TimerCallback oneShotCallback;
private readonly Timer timer;
private bool isActive;
public OneShotTimer(TimerCallback oneShotCallback, int dueTime = Timeout.Infinite)
{
this.oneShotCallback = oneShotCallback;
this.isActive = dueTime != Timeout.Infinite;
this.timer = new Timer(callback, this, dueTime, Timeout.Infinite);
}
public void Dispose()
{
timer.Dispose();
}
public void Start(int dueTime)
{
if (!tryChange(true, dueTime))
throw new InvalidOperationException("The timer has already been started");
}
public bool TryCancel()
{
return tryChange(false, Timeout.Infinite);
}
public bool tryChange(bool targetIsActive, int dueTime)
{
bool result = false;
lock (sync)
{
if (isActive != targetIsActive)
{
result = true;
isActive = targetIsActive;
timer.Change(dueTime, Timeout.Infinite);
}
}
return result;
}
private static void callback(object state)
{
var oneShotTimer = (OneShotTimer)state;
if (oneShotTimer.TryCancel())
oneShotTimer.oneShotCallback(oneShotTimer);
}
}