CancellationTokenSource.Cancel 引發 ObjectDisposedException (CancellationTokenSource.Cancel throws an ObjectDisposedException)


問題描述

CancellationTokenSource.Cancel 引發 ObjectDisposedException (CancellationTokenSource.Cancel throws an ObjectDisposedException)

I have a class that owns a 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:

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. Documentation says:

  

All public and protected members of CancellationTokenRegistration are thread‑safe and may be used concurrently from multiple threads, with the exception of Dispose, which must only be used when all other operations on the CancellationTokenRegistration have completed.

I'm not sure what to do at this moment. Wrap CancelToken in a lock? 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.

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.

‑‑‑‑‑

參考解法

方法 1:

This isn't really interesting but I wrapped Cancel and Dispose into a try‑catch that swallows ObjectDisposedException and haven't had problems since.

方法 2:

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:

_tokenSource.Cancel ();
_tokenSource.Dispose ();

is executed in one thread, then there is a Context Switch between threads before executing _tokenSource = null; and then another thread tries to run again the _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 = 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; 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.

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.

(by Dan AbramovDan AbramovMarc Cayuela)

參考文件

  1. CancellationTokenSource.Cancel throws an ObjectDisposedException (CC BY‑SA 3.0/4.0)

#race-condition #cancellationtokensource #dispose #xamarin.ios #C#






相關問題

Javascript 和 DOM 事件交互和可能的競爭條件 (Javascript and DOM event interaction and possible race conditions)

防禦 System.Collections.Concurrent.ConcurrentDictionary 中的競爭條件 (Defending against race conditions in System.Collections.Concurrent.ConcurrentDictionary)

可能一次在 PHP 中多次寫入同一個文件? (Potentially write to same file in PHP multiple times at once?)

靜態線程安全 (Thread safety of static)

CancellationTokenSource.Cancel 引發 ObjectDisposedException (CancellationTokenSource.Cancel throws an ObjectDisposedException)

如何處理 Web 應用邏輯和數據庫並發? (How to handle Web application logic and database concurrency?)

Linux IRQ 處理程序中的固有競爭條件 (Inherent race condition in Linux IRQ handlers)

SQL Server 進程隊列競爭條件 (SQL Server Process Queue Race Condition)

WCF 服務僅在客戶端收到結果時才寫入日誌 (WCF service writes log only if client receives results)

將 SWT 與 JOGL 一起使用時發生隨機崩潰(競爭條件?) (Random crashes when using SWT with JOGL (race condition?))

std::shared_ptr 的線程安全 (Thread safety with std::shared_ptr)

如何更新 JSON 類型列中的特定值 (How can I update specific value from my JSON type column)







留言討論