抛出ObjectDisposedException的代码有什么问题?

时间:2016-12-28 10:49:44

标签: c# semaphore

我很难理解这种情况下的实际行为。在预期但之后SemaphoreSlim被处置时不执行任务实际上发生了什么?它抛出以下异常 - System.ObjectDisposedException {"The semaphore has been disposed."}

我有一个类库 -

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();
                    }
                });
            }
        }
    }
}

我正在使用它 -

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();
    }
}

有人可以对此有所了解吗?提前谢谢。

3 个答案:

答案 0 :(得分:11)

问题在于您的using声明。这就是事情的发生方式:

  • 创建信号量
  • 启动在后台运行的任务
  • 处置信号量
  • 任务尝试使用信号量...但不能,因为它已被处理

选项:

  • 只需删除using语句(这样您就不会丢弃信号量,但除非您使用此非常,否则这不会出现问题)
  • 将您的方法更改为阻止(在using语句内),直到完成所有任务,例如使用Parallel.ForEach代替直接调用Task.Factory.StartNew
  • 更改代码以处理只在所有其他任务完成后执行的任务中的信号量

答案 1 :(得分:3)

您的信号量位于using块的末尾,但由在其中创建的仍在运行的任务使用。
我建议将信号量提升到班级:

public class ParallelProcessor
{
    private Action[] actions;
    private SemaphoreSlim concurrencySemaphore;

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

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

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

或替代方法,RunAllActions将阻止所有操作完成:

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 (var concurrencySemaphore = new SemaphoreSlim(maxConcurrency))
        {
            Task.WaitAll(actions.Select(a => Task.Run(() =>
                {
                    concurrencySemaphore.Wait();
                    try { a(); }
                    finally { concurrencySemaphore.Release(); }
                })).ToArray());
        }
    }
}

答案 2 :(得分:0)

我认为问题是处理已在using语句中的concurrencySemaphore。

使用的主要用途是,它会自动添加try和finally,最后它会处理使用下的对象。

https://www.codeproject.com/Articles/6564/Understanding-the-using-statement-in-C

您的案例的解决方案是删除使用或删除finally语句