Как отслеживать / ожидать массив объектов?

#c#

#c#

Вопрос:

Я использую параллельный пакет для хранения набора объектов. Я хочу реализовать что-то вроде if (объект присутствует), верните его, иначе подождите, пока он не освободится, если он не освободится в определенное время, генерирует исключение.

если (объект был возвращен) добавить в сумку

Я думал использовать мониторы, но монитор может ждать определенного объекта. Я хочу подождать, пока любой из них не освободится. Как я могу это реализовать?

Комментарии:

1. Вы что-нибудь пробовали?

2. Вот статья msdn, объясняющая, как использовать параллельный пакет для создания пула объектов. Поведение создает новый объект вместо ожидания. msdn.microsoft.com/en-us/library/ff458671(v=vs.110).aspx

3. Я размышляю над этими строками, возвращая объекты, я проверяю, опустел ли пакет. Если да, я устанавливаю переменную true. Следующий поток, если приходит для объекта и находит переменную true , ему нужно подождать определенное время, пока она не станет true . Мне нужно использовать блокировку здесь, если два потока запрашивают объект

4. @Gusdor: Из-за ограничения ресурсов я не могу создать столько объектов, сколько необходимо. В любом случае спасибо за статьи.

Ответ №1:

Расширение примера msdn, найденного здесь:

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

РЕДАКТИРОВАТЬ — пример использования с оболочкой идиомы ‘Context’

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

Обратите внимание на задержку, создаваемую механизмами синхронизации потоков.

Комментарии:

1. Вместо того, чтобы пытаться, что-то вроде While(!_objects . TryPeek (out item)) также поможет. Но чтобы подождать определенное количество времени, я считаю, что для этого нам нужно использовать запуск на основе событий.

2. @user3747969 Я не большой поклонник идиомы Get / Put. Похоже, что где-то маленький упс может постоянно уменьшать ваш пул. Я ожидаю, что вы бы обернули пул чем-то конкретным, если бы хотели создать using(var context = Pool.GetContext()){/*work*/} модель программирования.

3. @user3747969 проблема в TryPeek том, что он не удаляет объект из пакета. Вы должны где-то поддерживать список доступных объектов — использование содержимого пакета — хороший способ сделать это.

Ответ №2:

Попробуйте использовать семафоры для операций, которые вы хотите выполнить. .NET имеет две реализации для семафоров. Semaphore и SemaphoreSlim, оба могут использоваться для реализации множества потоков, пытающихся получить доступ к пулу ресурсов.