Это на самом деле довольно тривиально, используя TPL и новые коллекции в System.Collections.Concurrent
.
Для ваших нужд BlockingCollection<T>
это то, что я бы порекомендовал.По умолчанию он использует ConcurrentQueue<T>
в качестве базового хранилища, которое идеально подходит для того, что вы хотите.
var queue = new BlockingCollection<Message>();
Чтобы установить некоторый код, работающий с этими сообщениями, и контролировать, сколько из них может выполнятьсяпараллельно это так просто:
//Set max parallel Tasks
var options = new ParallelOptions
{
MaxDegreeOfParallelism = 10
};
Parallel.ForEach(queue.GetConsumingEnumerable(), options, msg =>
{
//Do some stuff with this message
});
Так что же здесь происходит?Ну ...
Вызов GetConsumingEnumerable()
будет фактически блокироваться, пока в queue
не будет что-то потреблять.Это здорово, потому что не требуется никакого дополнительного кода для сигнализации о том, что новая работа готова к выполнению.Скорее, когда queue
заполняется, новая задача с вашим (анонимным) делегатом будет запущена с элементом.
Объект ParallelOptions
позволяетВы можете контролировать, как работает Parallel.ForEach
.В этом случае вы говорите, что никогда не хотите, чтобы одновременно выполнялось более 10 задач. Важно отметить, что Tasks! = Threads .Детали мрачны, но, разумеется, под капотом происходит много оптимизаций.Это все подключаемо, но это не для слабонервных.
Очевидно, что здесь много деталей, которые я не раскрыл, но, надеюсь, вы сможете увидеть, насколько просто и выразительно использовать библиотеку параллельных задачможет быть.