Инкрементальная переменная контроля параллелизма

У меня есть следующая функция TPL:

int arrayIndex = 0;

Dictionary < string, int > customModel = new Dictionary < string, int > ();

    Task task = Task.Factory.StartNew(() =>

        // process each employee holiday

        Parallel.ForEach < EmployeeHolidaysModel > (holidays,
            new ParallelOptions() {
                MaxDegreeOfParallelism = System.Enviroment.ProcessorCount
            },
            item => {

                customModel.Add(item.HolidayName, arrayIndex);

                // increment the index
                arrayIndex++;

            })
    );

    //wait for all Tasks to finish
    Task.WaitAll(task);

Проблема в том, что arrayIndex не будет иметь уникальных значений из-за параллелизма.

Есть ли способ управлять переменной arrayIndex, чтобы между параллельными задачами значение было уникальным?

По сути, в моем customModel я не могу иметь повторяющееся значение arrayIndex.

Цените любую помощь.


person VAAA    schedule 08.07.2014    source источник
comment
Во-первых: почему вы хотите делать это параллельно? Достаточно ли у вас выходных, чтобы увидеть преимущества параллелизма? Для небольших наборов данных накладные расходы на использование нескольких потоков намного превышают преимущества, которые вы получаете от распараллеливания работы.   -  person Daniel Mann    schedule 08.07.2014
comment
Я просто помещаю образец, но там много вычислений, и получение данных занимает около 8 секунд. Мне нужно, чтобы это было быстрее   -  person VAAA    schedule 08.07.2014
comment
Почему ваш объект customModel должен быть словарем? Не лучше ли вместо этого позволить объекту отслеживать индексы? Мне просто кажется странным использовать строку в качестве ключа и индекс в качестве значения, но, может быть, вы можете уточнить это и объяснить, почему.   -  person Thomas Lindvall    schedule 08.07.2014


Ответы (2)


Здесь три проблемы:

  1. Вы пишете в общие переменные (как int, так и словарь). Это небезопасно. Вы должны либо синхронизировать, либо использовать потокобезопасные коллекции.
  2. Объем работы, которую вы выполняете за итерацию, настолько мал, что накладные расходы на параллелизм будут на несколько порядков больше. Это не лучший случай для параллелизма. Ожидайте серьезных замедлений.
  3. Вы запускаете задачу, затем ждете ее. Что вы имели в виду, чтобы сделать это?

Я думаю, вам нужно базовое руководство по многопоточности. Это очень основные вопросы. Вы не будете получать удовольствие от использования многопоточности на вашем нынешнем уровне знаний...

person usr    schedule 08.07.2014

Вам потребуется использовать Interlocked.Increment(). Вероятно, вам также следует использовать ConcurrentDictionary, чтобы быть в безопасности, предполагая, что это не просто пример -код, который вы придумали для вопроса.

Точно так же Task здесь не нужен, так как вы просто ждете, пока он закончит заполнение customModel. Очевидно, ваш сценарий может быть более сложным.

Но учитывая код, который вы разместили, я бы сделал что-то вроде:

int arrayIndex = 0;

ConcurrentDictionary<string,int> customModel
        = new ConcurrentDictionary<string,int>();

Parallel.ForEach<EmployeeHolidaysModel>(
    holidays,
    new ParallelOptions() {
        MaxDegreeOfParallelism = System.Enviroment.ProcessorCount
    },
    item => customModel.TryAdd(
        item.HolidayName,
        Interlocked.Increment(ref arrayIndex)
    )
);

NowYouCanDoSomethingWith(customModel);
person David Rubin    schedule 08.07.2014