Создание экземпляров закрытых объектов для использования в System.Threading.Tasks.Task? - PullRequest
1 голос
/ 07 февраля 2012

Допустим, у меня есть бизнес-объект, создание которого очень дорого, и я бы никогда не хотел создавать в своем приложении более 10 экземпляров этого объекта.Таким образом, это означало бы, что я бы никогда не хотел, чтобы одновременно выполнялось более 10 рабочих потоков.

Я бы хотел использовать новые System.Threading.Tasks для создания задачи, подобной этой:

 var task = Task.Factory.StartNew(() => myPrivateObject.DoSomethingProductive());

Существует ли пример, показывающий, как:

  1. создать «пул объектов» для использования в TaskFactory?
  2. ограничить TaskFactory указанным числом потоков?
  3. заблокировать экземпляр в пуле объектов, чтобы он мог использоваться только одной задачей за раз?

Ответ Игби привел меня к этому отличному сообщению в блоге от Джастина Этериджа.который затем побудил меня написать этот пример:

using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;

namespace MyThreadedApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            // build a list of 10 expensive working object instances
            var expensiveStuff = new BlockingCollection<ExpensiveWorkObject>();
            for (int i = 65; i < 75; i++)
            {
                expensiveStuff.Add(new ExpensiveWorkObject(Convert.ToChar(i)));
            }
            Console.WriteLine("{0} expensive objects created", expensiveStuff.Count);
            // build a list of work to be performed
            Random r = new Random();
            var work = new ConcurrentQueue<int>();
            for (int i = 0; i < 1000; i++)
            {
                work.Enqueue(r.Next(10000));
            }
            Console.WriteLine("{0} items in work queue", work.Count);
            // process the list of work items in fifteen threads
            for (int i = 1; i < 15; i++)
            {
                Task.Factory.StartNew(() =>
                {
                    while (true)
                    {
                        var expensiveThing = expensiveStuff.Take();
                        try
                        {
                            int workValue;
                            if (work.TryDequeue(out workValue))
                            {
                                expensiveThing.DoWork(workValue);
                            }
                        }
                        finally
                        {
                            expensiveStuff.Add(expensiveThing);
                        }
                    }
                });
            }
        }
    }
}

class ExpensiveWorkObject
{
    char identity;

    public void DoWork(int someDelay)
    {
        System.Threading.Thread.Sleep(someDelay);
        Console.WriteLine("{0}: {1}", identity, someDelay);
    }

    public ExpensiveWorkObject(char Identifier)
    {
        identity = Identifier;
    }
}

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

1 Ответ

2 голосов
/ 07 февраля 2012

Две мысли:

Ограниченный планировщик параллелизма

Можно использовать настраиваемый планировщик задач, который ограничивает количество одновременных задач.Внутренне он будет выделять до n экземпляров задач.Если вы передадите ему больше задач, чем у него есть доступных экземпляров, он поместит их в очередь.Добавление пользовательских планировщиков, подобных этому, является конструктивной особенностью TPL.

Здесь является хорошим примером такого планировщика.Я успешно использовал модифицированную версию этого.

Пул объектов

Другой вариант - использовать пул объектов.Это очень похожая концепция, за исключением того, что вместо того, чтобы устанавливать ограничение на уровне задач, вы устанавливаете его на число экземпляров объекта и заставляете задачи ждать, пока свободный экземпляр станет доступным.Преимущество этого заключается в уменьшении накладных расходов на создание объекта, но вы должны убедиться, что объект написан так, чтобы его экземпляры могли быть переработаны.Вы можете создать пул объектов вокруг параллельной коллекции производитель-потребитель, такой как ConcurrentStack , где потребитель добавляет экземпляр обратно в коллекцию после его завершения.

...