[英]CancellationTokenSource.Cancel throws an ObjectDisposedException
我有一个拥有CancellationTokenSource
的类。
public class GrabboxCell : UICollectionViewCell
{
CancellationTokenSource _tokenSource = new CancellationTokenSource ();
// ...
}
我正在使用当前令牌来启动一些长时间运行的操作。
我的目标还需要支持“回收”。 想想转世。 必须取消在前一生中开始的所有长时间运行的操作。
在这种情况下,我在源上调用Cancel
和Dispose
,并发出新的令牌源:
void CancelToken (bool createNew)
{
_tokenSource.Cancel ();
_tokenSource.Dispose ();
_tokenSource = null;
if (createNew) {
_tokenSource = new CancellationTokenSource ();
}
}
我在两个地方调用此方法:当我想要令牌到期时以及何时处理此类。
public override void PrepareForReuse ()
{
CancelToken (true);
base.PrepareForReuse ();
}
protected override void Dispose (bool disposing)
{
CancelToken (false);
base.Dispose (disposing);
}
有时我从Dispose
方法调用_tokenSource.Cancel ()
时会遇到ObjectDisposedException
。 文件说:
CancellationTokenRegistration
所有公共成员和受保护成员都是线程安全的,并且可以从多个线程同时使用,但Dispose
除外,它只能在CancellationTokenRegistration
上的所有其他操作都已完成时使用。
我不知道此刻该怎么做。 在lock
包裹CancelToken
?
竞争状况究竟发生在哪里以及如何减轻竞争?
我确信PrepareForReuse
总是在同一个线程上调用,但是可以在另一个线程上调用Dispose
。
如果这是有用的,我运行Mono而不是.NET Framework,但我很确定它们应该具有与取消令牌相同的语义。
这不是很有趣,但我将Cancel
和Dispose
包装成一个try-catch,它吞下了ObjectDisposedException
并且从那以后就没有问题了。
线程安全(单独)操作并不意味着您的操作序列一次执行。 更具体地说,由于PrepareForReuse
可以在与Dispose
不同的线程中运行,那么可能发生的是:
_tokenSource.Cancel ();
_tokenSource.Dispose ();
在一个线程中执行,然后在执行_tokenSource = null;
之前在线程之间有一个Context Switch _tokenSource = null;
然后另一个线程再次尝试运行_tokenSource.Cancel ()
。 但是tokenSource已经被处理掉而没有重新生成,因为第一个线程没有到达取消的最后一段代码:
_tokenSource = new CancellationTokenSource ();
如果你不时得到NullPointerException
,如果上下文切换发生在_tokenSource = null;
之后,我也不会感到惊讶_tokenSource = null;
而不是之前我解释过(也有可能)。
为了解决这个问题,我会锁定你的Cancel
方法,这样线程就不能在另一个完成之前运行方法的任何部分。
另外,为了保护NullPointerException
,只有在PrepareForReuse
之前调用Dispose
方法时才会发生这种情况,您可以使用Null-Conditional运算符 。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.