What is wrong in code to

It's making me little hard to understand the actual behavior in this scenario. What is actually happening to not executing the task when it should but later when SemaphoreSlim is disposed. It throws me following exception- System.ObjectDisposedException {"The semaphore has been disposed."}

I have a class library like -

public class ParallelProcessor
{
    private Action[] actions;
    private int maxConcurrency;

    public ParallelProcessor(Action[] actionList, int maxConcurrency)
    {
        this.actions = actionList;
        this.maxConcurrency = maxConcurrency;
    }

    public void RunAllActions()
    {
        if (Utility.IsNullOrEmpty<Action>(actions))
            throw new Exception("No Action Found!");

        using (SemaphoreSlim concurrencySemaphore = new SemaphoreSlim(maxConcurrency))
        {
            foreach (Action action in actions)
            {
                Task.Factory.StartNew(() =>
                {
                    concurrencySemaphore.Wait();
                    try
                    {
                        action();
                    }
                    finally
                    {
                        concurrencySemaphore.Release();
                    }
                });
            }
        }
    }
}

And using it like-

class Program
{
    static void Main(string[] args)
    {
        int maxConcurrency = 3;
        Action[] actions = new Action[] { () => Console.WriteLine(1), () => Console.WriteLine(2), () => Console.WriteLine(3) }; //Array.Empty<Action>();

        ParallelProcessor processor = new ParallelProcessor(actions, maxConcurrency);

        processor.RunAllActions();

        Console.ReadLine();
    }
}

Could anybody please showered some light on it? Thanks in advance.

Jon Skeet
people
quotationmark

The problem is your using statement. This is how things are happening:

  • Create the semaphore
  • Start tasks running in the background
  • Dispose of the semaphore
  • Tasks try to use the semaphore... but can't, because it's disposed

Options:

  • Just remove the using statement (so you don't dispose of the semaphore, but that's unlikely to be a problem unless you're using this really heavily)
  • Change your method to block (inside the using statement) until all the tasks have completed, e.g. by using Parallel.ForEach instead of calling Task.Factory.StartNew directly
  • Change your code to dispose of the semaphore in a task which will only execute after all the other tasks have completed

people

See more on this question at Stackoverflow