#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, оба могут использоваться для реализации множества потоков, пытающихся получить доступ к пулу ресурсов.