繁体   English   中英

CancellationTokenSource.Cancel抛出ObjectDisposedException

[英]CancellationTokenSource.Cancel throws an ObjectDisposedException

我有一个拥有CancellationTokenSource的类。

public class GrabboxCell : UICollectionViewCell
{
    CancellationTokenSource _tokenSource = new CancellationTokenSource ();

    // ...
}

我正在使用当前令牌来启动一些长时间运行的操作。

我的目标还需要支持“回收”。 想想转世。 必须取消在前一生中开始的所有长时间运行的操作。

在这种情况下,我在源上调用CancelDispose ,并发出新的令牌源:

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,但我很确定它们应该具有与取消令牌相同的语义。

这不是很有趣,但我将CancelDispose包装成一个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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM