【问题标题】:How to monitor/wait on array of objects?如何监视/等待对象数组?
【发布时间】:2014-06-17 09:59:43
【问题描述】:

我正在使用并发包来存储一组对象。我想实现类似的东西 如果(存在一个对象) 把它返还 否则等到释放,如果在特定时间没有释放则抛出异常。

如果(对象已返回) 加入购物袋

我正在考虑使用监视器,但监视器可以等待特定对象。我想等到他们中的任何一个有空。如何实现?

【问题讨论】:

  • 你有没有尝试过?
  • 这里有一篇msdn文章解释了如何使用并发包创建对象池。该行为创建一个新对象而不是等待。 msdn.microsoft.com/en-us/library/ff458671%28v=vs.110%29.aspx
  • 我正在考虑这些线路,在返回对象时,我正在检查包是否已经变空。如果是,我将变量设置为 true。下一个线程如果来找一个对象并发现变量为真,它需要等待一个特定的时间直到它变为真。如果两个线程来请求一个对象,我需要在这里使用锁定
  • @Gusdor :由于资源限制,我无法根据需要创建尽可能多的对象。无论如何,感谢您的文章。

标签: c#


【解决方案1】:

扩展msdn示例found here

public class FiniteObjectPool<T>: IDisposable
{
    System.Threading.AutoResetEvent m_Wait = new System.Threading.AutoResetEvent(false);

    private ConcurrentBag<T> _objects;

    public FiniteObjectPool()
    {
        _objects = new ConcurrentBag<T>();
    }

    public T GetObject()
    {
        T item;
        while(!_objects.TryTake(out item))
        {
            m_Wait.WaitOne(); //an object was not available, wait until one is
        }

        return item;
    }

    public void PutObject(T item)
    {
        _objects.Add(item);
        m_Wait.Set(); //signal a waiting thread that object may now be available
    }

    public void Dispose()
    {
        m_Wait.Dispose();
    }
}

编辑 - 使用“上下文”惯用语包装器的示例用法

class Program
{
    public class FiniteObjectPoolContext<T>: IDisposable
    {
        FiniteObjectPool<T> m_Pool = new FiniteObjectPool<T>();
        public T Value { get; set; }

        public FiniteObjectPoolContext(FiniteObjectPool<T> pool)
        {
            m_Pool = pool;
            Value = pool.GetObject(); //take an object out - this will block if none is available
        }

        public void Dispose()
        {
            m_Pool.PutObject(Value); //put the object back because this context is finished
        }
    }
    static void Main(string[] args)
    {
        FiniteObjectPool<int> pool = new FiniteObjectPool<int>();

        for (int i = 0; i < 10; i++)
        {
            pool.PutObject(i);
        }

        List<Task> tasks = new List<Task>();
        for (int i = 0; i < 20; i++)
        {
            int id = i;
            tasks.Add(Task.Run(() =>
                {
                    Console.WriteLine("Running task " + id);
                    using (var con = new FiniteObjectPoolContext<int>(pool))
                    {
                        Console.WriteLine("Task " + id + " got object from pool: " + con.Value);
                        System.Threading.Thread.Sleep(5000);
                        Console.WriteLine("Task " + id + " is finished with pool object: " + con.Value);
                    }
                }));
        }

        Task.WaitAll(tasks.ToArray());
        Console.WriteLine("DONE");
        Console.ReadLine();
    }
}

注意线程同步机制注入的延迟。

【讨论】:

  • 代替 TryTake ,像 While(!_objects.TryPeek(out item)) 这样的东西也可以解决问题。但是要等待特定的时间,我相信我们需要使用基于事件的触发。
  • @user3747969 我不是 Get/Put 成语的忠实粉丝。似乎某处的小 whoopsie 可能会永久减少您的游泳池。如果您想创建 using(var context = Pool.GetContext()){/*work*/} 编程模型,我希望您可以使用特定的东西来包装池。
  • @user3747969 TryPeek 的问题是它不会从包中取出对象。您必须在某处维护可用对象的列表 - 使用包中的内容是这样做的好方法。
【解决方案2】:

尝试使用信号量来执行您想要执行的操作。 .NET 有两种信号量实现。 Semaphore 和 SemaphoreSlim,两者都可用于实现许多线程尝试访问资源池。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-01-17
    • 2016-10-09
    • 1970-01-01
    • 1970-01-01
    • 2011-07-17
    • 2014-10-31
    • 2020-09-26
    • 1970-01-01
    相关资源
    最近更新 更多