[英]CancellationTokenSource.Cancel throws an ObjectDisposedException
I have a class that owns a CancellationTokenSource
. 我有一个拥有
CancellationTokenSource
的类。
public class GrabboxCell : UICollectionViewCell
{
CancellationTokenSource _tokenSource = new CancellationTokenSource ();
// ...
}
I'm using current token to start some long-running operations. 我正在使用当前令牌来启动一些长时间运行的操作。
My object also needs to support “recycling”. 我的目标还需要支持“回收”。 Think reincarnation.
想想转世。 All long-running operations started during previous life must be cancelled.
必须取消在前一生中开始的所有长时间运行的操作。
In this case I call Cancel
and Dispose
on the source, and issue a new token source: 在这种情况下,我在源上调用
Cancel
和Dispose
,并发出新的令牌源:
void CancelToken (bool createNew)
{
_tokenSource.Cancel ();
_tokenSource.Dispose ();
_tokenSource = null;
if (createNew) {
_tokenSource = new CancellationTokenSource ();
}
}
I call this method in two places: when I want the token to expire and when this class is disposed. 我在两个地方调用此方法:当我想要令牌到期时以及何时处理此类。
public override void PrepareForReuse ()
{
CancelToken (true);
base.PrepareForReuse ();
}
protected override void Dispose (bool disposing)
{
CancelToken (false);
base.Dispose (disposing);
}
Sometimes I'm getting an ObjectDisposedException
when calling _tokenSource.Cancel ()
from my Dispose
method. 有时我从
Dispose
方法调用_tokenSource.Cancel ()
时会遇到ObjectDisposedException
。 Documentation says: 文件说:
All public and protected members of
CancellationTokenRegistration
are thread-safe and may be used concurrently from multiple threads, with the exception ofDispose
, which must only be used when all other operations on theCancellationTokenRegistration
have completed.CancellationTokenRegistration
所有公共成员和受保护成员都是线程安全的,并且可以从多个线程同时使用,但Dispose
除外,它只能在CancellationTokenRegistration
上的所有其他操作都已完成时使用。
I'm not sure what to do at this moment. 我不知道此刻该怎么做。 Wrap
CancelToken
in a lock
? 在
lock
包裹CancelToken
?
Where exactly does the race condition happen and how to mitigate it? 竞争状况究竟发生在哪里以及如何减轻竞争?
I know for sure that PrepareForReuse
is always called on the same thread, but Dispose
may be called on a different one. 我确信
PrepareForReuse
总是在同一个线程上调用,但是可以在另一个线程上调用Dispose
。
If this is of any use, I'm running Mono and not .NET Framework but I'm pretty sure they should have the same semantics regarding cancellation tokens. 如果这是有用的,我运行Mono而不是.NET Framework,但我很确定它们应该具有与取消令牌相同的语义。
这不是很有趣,但我将Cancel
和Dispose
包装成一个try-catch,它吞下了ObjectDisposedException
并且从那以后就没有问题了。
The operations being thread safe (individually) does not imply that your sequence of operations are executed at once. 线程安全(单独)操作并不意味着您的操作序列一次执行。 More specifically, since
PrepareForReuse
could run in a different thread as Dispose
, then what could happen is that this: 更具体地说,由于
PrepareForReuse
可以在与Dispose
不同的线程中运行,那么可能发生的是:
_tokenSource.Cancel ();
_tokenSource.Dispose ();
is executed in one thread, then there is a Context Switch between threads before executing _tokenSource = null;
在一个线程中执行,然后在执行
_tokenSource = null;
之前在线程之间有一个Context Switch _tokenSource = null;
and then another thread tries to run again the _tokenSource.Cancel ()
. 然后另一个线程再次尝试运行
_tokenSource.Cancel ()
。 But the tokenSource was disposed of already and not regenerated, since the first thread did not reach the last block of code of the cancel: 但是tokenSource已经被处理掉而没有重新生成,因为第一个线程没有到达取消的最后一段代码:
_tokenSource = new CancellationTokenSource ();
I would not be surprised either if you get from time to time a NullPointerException
, if the context switch happened just after _tokenSource = null;
如果你不时得到
NullPointerException
,如果上下文切换发生在_tokenSource = null;
之后,我也不会感到惊讶_tokenSource = null;
instead of before as I explained (it is also possible). 而不是之前我解释过(也有可能)。
To solve this problem I would lock your Cancel
method so that the threads cannot run any part of the method before the other was finished. 为了解决这个问题,我会锁定你的
Cancel
方法,这样线程就不能在另一个完成之前运行方法的任何部分。
Also, to protect for the NullPointerException
, which can only happen if your method Dispose
is called before PrepareForReuse
, you can use the Null-Conditional Operator . 另外,为了保护
NullPointerException
,只有在PrepareForReuse
之前调用Dispose
方法时才会发生这种情况,您可以使用Null-Conditional运算符 。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.