我有一种情况,我必须同步调用异步方法,并按如下方式完成:
MyInternalLibraryClass::DoSomething(const std::vector<MyObjectImpl>& objs);
如果取消了取消令牌,则尽管通过使用语句激活,但任务中的一次性用品不会被处置。
以下程序说明了问题:
obj.asyncMethod().Wait(myCancelToken)
似乎Wait方法只是在取消时杀死任务线程,而不是在该线程内触发异常并让它自然终止。问题是为什么?
答案 0 :(得分:5)
您已取消Wait(timeout.Token)
返回的任务,而不是LongRunningTask
返回的任务,如果您要取消该令牌,请将令牌传递给Task.Run
并使用await Task.Delay
}而不是Thread.Sleep
并将令牌传递给那里。
static void Main(string[] args)
{
try
{
var timeout = new CancellationTokenSource(TimeSpan.FromMilliseconds(100));
LongRunningTask(timeout.Token).Wait();
}
catch (AggregateException error)
{
// handling timeout is logically okay, but expect nothing to be leaked
}
Console.WriteLine("Leaked Instances = {0}", DisposableResource.Instances);
Console.ReadLine();
}
static async Task LongRunningTask(CancellationToken token)
{
using (var resource = new DisposableResource())
{
await Task.Run(async () => await Task.Delay(1000, token), token);
}
}
public class DisposableResource : IDisposable
{
public static int Instances = 0;
public DisposableResource()
{
Instances++;
}
public void Dispose()
{
Instances--;
}
}
注意一旦长时间运行的操作完成,using statment仍然会处理资源。运行此示例:
static void Main(string[] args)
{
try {
var timeout = new CancellationTokenSource(TimeSpan.FromMilliseconds(100));
LongRunningTask().Wait(timeout.Token);
} catch (OperationCanceledException error) {
// handling timeout is logically okay, but expect nothing to be leaked
}
Console.WriteLine("Leaked Instances = {0}", DisposableResource.Instances);
Console.ReadKey();
}
static async Task LongRunningTask()
{
using (var resource = new DisposableResource())
{
await Task.Run(() => Thread.Sleep(1000));
}
}
public class DisposableResource : IDisposable
{
public static int Instances = 0;
public DisposableResource()
{
Instances++;
}
public void Dispose()
{
Instances--;
Console.WriteLine("Disposed resource. Leaked Instances = {0}", Instances);
}
}
<强>输出强>
泄漏的实例= 1
处置资源。泄漏的实例= 0
答案 1 :(得分:4)
似乎Wait方法只是在取消时杀死任务线程而不是在该线程中触发异常
你错了,当你取消发生的唯一事情是你停止等待Wait(myCancelToken)
完成时,任务仍然在后台运行。
要取消后台任务,您必须将取消令牌传递到链中的所有方法。如果您希望最内层(长时间运行的层)提前停止,则代码必须在其代码中调用token.ThrowIfCancellationRequested()
。