我有一个拥有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);
}有时,当从我的ObjectDisposedException方法调用_tokenSource.Cancel ()时,我会得到一个Dispose。文件上说:
CancellationTokenRegistration的所有公共成员和受保护成员都是线程安全的,可以从多个线程并发使用,但Dispose除外,只有在CancellationTokenRegistration上的所有其他操作完成后才能使用。
我现在不知道该怎么办。将CancelToken包装在lock中
比赛条件究竟是在哪里发生的,如何加以缓解?
我确信,PrepareForReuse总是在同一个线程上被调用,但是Dispose可能在另一个线程上被调用。
如果这是有用的,我运行的是Mono,而不是.NET框架,但我非常肯定,它们在取消令牌方面应该具有相同的语义。
发布于 2013-02-08 19:55:52
这并不是很有趣,但我把Cancel和Dispose包装成了一个尝试捕获,燕子ObjectDisposedException,从那以后就没有问题了。
发布于 2019-07-08 07:53:31
线程安全的操作(单独的)并不意味着您的操作序列是立即执行的。更具体地说,由于PrepareForReuse可以以Dispose的形式在不同的线程中运行,所以可能发生的情况是:
_tokenSource.Cancel ();
_tokenSource.Dispose ();在一个线程中执行,然后在执行_tokenSource = null;之前在线程之间切换一个上下文,然后另一个线程尝试再次运行_tokenSource.Cancel ()。但是tokenSource已经被释放,没有重新生成,因为第一个线程没有到达cancel的最后一个代码块:
_tokenSource = new CancellationTokenSource ();如果您不时地得到一个NullPointerException,如果上下文切换发生在_tokenSource = null;之后,而不是像我解释的那样(这也是可能的),我也不会感到惊讶。
为了解决这个问题,我将锁定您的Cancel方法,以便线程在另一个方法完成之前不能运行该方法的任何部分。
此外,要保护NullPointerException,只有在调用PrepareForReuse之前调用方法Dispose时才能进行保护,您可以使用https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/member-access-operators#null-conditional-operators--and-。
https://stackoverflow.com/questions/14661147
复制相似问题