我有一个遗留场景,其中ref bool
被用来向实现发送取消信号。现在,我想调用一个采用Task
实例的基于CancellationToken
的库方法,当布尔值更改值时,我也想取消该实例。
这是我必须使用的:
void Method(ref bool isCancelled)
{
while (!isCancelled)
{
...
DoThis();
DoThat();
...
}
}
这就是我想做的:
Task MethodAsync(ref bool isCancelled)
{
while (!isCancelled)
{
...
DoThis();
await DoTheNewThingAsync(isCancelled.ToCancellationToken());
DoThat();
...
}
}
ToCancellationToken()
当然在此上下文中不存在,仅用于表示意图。
我试图创建CancellationTokenSource
的自定义实现,但是该类中没有虚拟的东西可以使用。也无法直接创建自定义CancellationToken
,因为它是struct
并且无法继承。
我知道使用ref bool
是一种不好的做法,但是我目前无法更改依赖它的基础实现,因此我需要一种方法来将其值用作基于任务的调用的取消机制。
很复杂。由于某些原因:
ref
的参数传递给async
方法。您正在使用await
,但是要使用await
,您的方法需要标记为async
。并且async
方法不能具有ref
参数。例如,它将不会编译:async Task MethodAsync(ref bool isCancelled)
{
while (!isCancelled)
{
DoThis();
await DoTheNewThingAsync(isCancelled.ToCancellationToken());
DoThat();
}
}
会给您编译器错误:
CS1988:异步方法不能具有ref,in或out参数
ref
参数。我考虑过使用Timer
检查变量。像这样的东西:public static CancellationToken ToCancellationToken(ref bool isCancelled)
{
var tokenSource = new CancellationTokenSource();
var timer = new System.Timers.Timer()
{
AutoReset = true,
Interval = 100
};
timer.Elapsed += (source, e) =>
{
if (isCancelled)
{
tokenSource.Cancel();
timer.Dispose();
}
};
timer.Enabled = true;
return tokenSource.Token;
}
但是这会给您带来编译器错误:
CS1628:在匿名方法,lambda表达式,查询表达式或局部函数内不能使用ref,out或in参数'isCancelled'
我没有看到其他通过引用将bool
放入事件处理程序的方法。
void Method(ref bool isCancelled)
{
var tokenSource = new CancellationTokenSource();
while (!isCancelled)
{
DoThis();
var mytask = DoTheNewThingAsync(tokenSource.Token);
while (true)
{
//wait for either the task to finish, or 100ms
if (Task.WaitAny(mytask, Task.Delay(100)) == 0)
{
break; //mytask finished
}
if (isCancelled) tokenSource.Cancel();
}
DoThat();
}
}
但是,这阻止了呼叫者,所以我不完全知道那怎么可能有用。但这就是您现有方法正在做的事情,所以也许可行?
我已经破解了一个可行的解决方案:
public static class TaskRefBoolCancellable
{
public static T SynchronousAwait<T>(Func<CancellationToken, Task<T>> taskToRun, ref bool isCancelled)
{
using (var cts = new CancellationTokenSource())
{
var runningTask = taskToRun(cts.Token);
while (!runningTask.IsCompleted)
{
if (isCancelled)
cts.Cancel();
Thread.Sleep(100);
}
return runningTask.Result;
}
}
}
void Method(ref bool isCancelled)
{
while (!isCancelled)
{
...
DoThis();
var result = TaskRefBoolCancellable.SynchronousAwait(DoTheNewThingAsync, ref isCancelled);
DoThat();
...
}
}
WARNING:此代码在调用线程上同步运行。因此,不能保证它会与代码的其他部分很好地配合,因为它会阻塞调用线程。另外,它轮询isCancelled
变量,使其无效并且取消不是立即进行的。
我认为这是一个权宜之计,因为您将ref bool isCancelled
替换为基于任务的适当取消。