Search code examples
c#warningsdisposeparallel.foreach

Access to disposed closure but no using() block


I'm getting this warning about

Access to disposed closure

But I don't see how that is happening in this code:

private BlockingCollection<PicInfo> listOfPicInfoObjectBC;
private List<PicInfo> CreatePicInfoObjects(List<string> pics)
{
    listOfPicInfoObjectBC = new BlockingCollection<PicInfo>();
    var cts = new CancellationTokenSource();

    try
    {
        var parallelOptions = new ParallelOptions 
        {
            CancellationToken = cts.Token, 
            MaxDegreeOfParallelism = 10
        };

        Parallel.ForEach(pics, parallelOptions, (pic, loopState) =>
        {
            ParalellizedCreatePicInfoObjects(pic);
            if (!cts.Token.IsCancellationRequested)
                ParalellizedCreatePicInfoObjects(pic);
            else
            {
                loopState.Stop();
                cts.Token.ThrowIfCancellationRequested();
            }

        });
    }

    catch (OperationCanceledException exp)
    {
        .......
    }

    finally
    {
        cts.Dispose();
    }

    return new List<PicInfo>(listOfPicInfoObjectBC);
}

I've seen this warning too in other SO posts, but all of them had to do something with the using() block (were it made sense) but which I don't have.

If this warning is correct, how could it happen here?

[Edit] Forgot to mention the warning happens on this if (!cts.Token.IsCancellationRequested) and cts.Token.ThrowIfCancellationRequested(); line.


Solution

  • If this warning is correct, how could it happen here?

    The compiler has no knowledge of when and how Parallel.ForEach will execute the passed delegate. All that can be inferred is that you pass an object to the lambda which causes it to be captured in the closure, thus it warns you that there exists a possibility that the object will be disposed prior to the delegate being executed, as it's being disposed in the finally block.

    In this particular case, the warning can be suppressed as you know that Parallel.ForEach will cause the immediate invocation of the delegate.