Одно из решений состоит из отличной Threading in C# E-book. В своем разделе об основных структурах автор делает практически то, что вы ищете in an example.
Следуйте по этой ссылке и прокрутите вниз до очереди производителя/потребителя.
В более позднем разделе он обращается к тому, что в то время как ConcurrentQueue также будет работать нормально, он выполняет хуже во всех случаях, ЗА ИСКЛЮЧЕНИЕМ в высококонкурентных сценариях. Но для вашего случая с низкой нагрузкой может быть лучше просто получить что-то, что легко работает. У меня нет личного опыта в заявлении из документа, но он там для вас, чтобы оценить.
Надеюсь, это поможет.
Edit2: по предложению Evk (спасибо!) Класс BlockingCollection выглядит так, как вы хотите. По умолчанию он использует ConcurrentQueue под капотом. Мне особенно нравится метод CompleteAdding, а также возможность использования CancellationTokens с ним. Сценарии «Завершение работы» не всегда правильно учитываются при блокировании вещей, но это верно для ИМО.
Редактировать 3: В соответствии с запросом образец того, как это будет работать с BlockingCollection. Я использовал foreach
и GetConsumingEnumerable
, чтобы сделать это еще более компактным для потребителя стороны проблемы:
using System.Collections.Concurrent;
private static void testMethod()
{
BlockingCollection<Action> myActionQueue = new BlockingCollection<Action>();
var consumer = Task.Run(() =>
{
foreach(var item in myActionQueue.GetConsumingEnumerable())
{
item(); // Run the task
}// Exits when the BlockingCollection is marked for no more actions
});
// Add some tasks
for(int i = 0; i < 10; ++i)
{
int captured = i; // Imporant to copy this value or else
myActionQueue.Add(() =>
{
Console.WriteLine("Action number " + captured + " executing.");
Thread.Sleep(100); // Busy work
Console.WriteLine("Completed.");
});
Console.WriteLine("Added job number " + i);
Thread.Sleep(50);
}
myActionQueue.CompleteAdding();
Console.WriteLine("Completed adding tasks. Waiting for consumer completion");
consumer.Wait(); // Waits for consumer to finish
Console.WriteLine("All actions completed.");
}
я добавил в Sleep() вызовов, так что вы можете увидеть, что вещи будут добавлены в то время как другие вещи потребляются , Вы также можете выбрать любое количество этого consumer
lambda (просто назовите его Action
, затем запустите Action
несколько раз) или цикл добавления. И в любое время вы можете позвонить Count
в сборнике, чтобы получить количество задач, которые НЕ запускаются. Предположительно, если это отличное от нуля, тогда выполняются ваши задачи-производители.
Посмотрите на BlockingCollection вместе с ConcurrentQueue, есть много примеров. Начните поток, который потребляет от BlockingCollection, и элементы к нему из вашего основного цикла. – Evk
Задача и пользовательский TaskScheduler, вероятно, сделают трюк, если вы хотите, чтобы ваши делегаты выполнялись последовательно. См. Http://www.infoworld.com/article/3063560/application-development/building-your-own-task-scheduler-in-c.html о том, как создать пользовательский TaskScheduler. Если вы не возражаете, чтобы ваши делегаты выполнялись параллельно, стандартный .NET TaskScheduler, вероятно, прекрасен. –
Рассмотрите возможность хранения всего одного потока и использования async-ожидания для создания асинхронного рабочего процесса. Помните, ** асинхронность не является параллелизмом **. Вы можете выполнять асинхронные рабочие процессы на одном потоке, точно так же, как вы можете готовить яйца и тосты одновременно, не нанимая двух поваров. –