многопоточность: как обрабатывать данные в векторе при заполнении вектора? - PullRequest
1 голос
/ 10 сентября 2010

У меня есть однопоточное приложение linux, которое я хотел бы сделать параллельным.Он читает файл данных, создает объекты и помещает их в вектор.Затем он вызывает интенсивные вычисления (.5 секунд +) для каждого объекта.Я хочу вызвать метод параллельно с созданием объекта.Пока я смотрел на qt и tbb, я открыт для других опций.

Я планировал запустить поток (ы), пока вектор был пуст.Каждый из них будет вызывать makeSolids (ниже), который имеет цикл while, который будет работать до тех пор, пока interpDone == true и все объекты в векторе не будут обработаны.Тем не менее, я n00b, когда дело доходит до потоков, и я искал готовое решение.

QtConcurrent::map(Iter begin,Iter end,function()) выглядит очень просто, но я не могу использовать его для вектора, которыйизменить размер, можно?И как я скажу ему ждать больше данных?

Я также посмотрел на tbb от intel, но, похоже, мой основной поток остановился, если бы я использовал parallel_for или parallel_while.Это воняет, так как их менеджер памяти был рекомендован (mmgt открытого каскада имеет низкую производительность при многопоточности).

/**intended to be called by a thread
\param start the first item to get from the vector
\param skip how many to skip over (4 for 4 threads)
*/
void g2m::makeSolids(uint start, uint incr) {
  uint curr = start;
  while ((!interpDone) || (lineVector.size() > curr)) {
    if (lineVector.size() > curr) {
      if (lineVector[curr]->isMotion()) {
        ((canonMotion*)lineVector[curr])->setSolidMode(SWEPT);
        ((canonMotion*)lineVector[curr])->computeSolid();
      }
      lineVector[curr]->setDispMode(BEST);
      lineVector[curr]->display();

      curr += incr;
    } else {
      uio::sleep(); //wait a little bit for interp
    }
  }
}

РЕДАКТИРОВАТЬ: Подводя итог, что является самым простым способом обработки вектора в то же время, что основной потокзаполняет вектор?

Ответы [ 3 ]

1 голос
/ 10 сентября 2010

Во-первых, чтобы извлечь выгоду из многопоточности, вам нужно найти одинаково медленные задачи для каждого потока.Вы сказали, что обработка каждого объекта занимает .5s +, сколько времени занимает чтение файла / создание объекта?Это может легко составить десятую или тысячную часть этого времени, и в этом случае ваш многопоточный подход принесет незначительную выгоду.Если это так (да, я скоро отвечу на ваш первоначальный вопрос, если это не так), то подумайте об одновременной обработке нескольких объектов.Поскольку ваша обработка занимает довольно много времени, накладные расходы на создание потока не очень значительны, так что вы можете просто сделать так, чтобы ваш основной поток чтения файлов / создания объектов порождал новый поток и направлял его на вновь созданный объект.Затем основной поток продолжает чтение / создание последующих объектов.Когда все объекты прочитаны / созданы и все потоки обработки запущены, основной поток «присоединяет» (ждет) рабочие потоки.Если это создаст слишком много потоков (тысяч), тогда наложите ограничение на то, насколько далеко может пройти основной поток: он может читать / создавать 10 объектов, затем присоединяться к 5, затем читать / создавать 10, присоединяться к 10, читать / создавать10, присоединитесь к 10 и т. Д., Пока не закончите.

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

В качестве альтернативы, вы можете использовать более сложный подход, который простовключает основной поток (который ОС создает при запуске вашей программы) и один рабочий поток, который должен запускаться основным потоком.Они должны координироваться с использованием мьютекса (переменной, обеспечивающей взаимоисключающий, что означает не-одновременный доступ к данным), и условной переменной, которая позволяет рабочему потоку эффективно блокировать, пока основной поток не предоставит больше работы.Термины - мьютекс и условная переменная - являются стандартными терминами в потоке POSIX, которые использует Linux, поэтому их следует использовать при объяснении конкретных библиотек, которые вас интересуют. В общем, рабочий поток ждет, пока основной поток чтения / созданияпередает сигнал пробуждения, указывающий, что другой объект готов к обработке.Вы можете захотеть иметь счетчик с индексом последнего полностью созданного, готового к обработке объекта, чтобы рабочий поток мог вести подсчет обработанных объектов и перемещаться по готовым, прежде чем снова проверять переменную условия.

0 голосов
/ 13 сентября 2010

@ Калеб: вполне - возможно, мне следовало бы подчеркнуть активные темы. Поток GUI всегда должен считаться одним.

0 голосов
/ 10 сентября 2010

Трудно сказать, задумывались ли вы об этой проблеме глубоко, и есть ли что-то большее, чем вы позволяете, или вы просто слишком думаете об этом, или вы просто опасаетесь многопоточности.

Быстрое чтение файла и создание объектов; один метод медленный. Зависимость каждого последовательного ctor зависит от результата предыдущего ctor - немного странно - но в остальном проблем с целостностью данных не возникает, поэтому, похоже, нет ничего, что нужно защищать мьютексами и тому подобным.

Почему это сложнее, чем что-то вроде этого (в грубом псевдокоде):

while (! eof)
{
    readfile;
    object O(data);
    push_back(O);
    pthread_create(...., O, makeSolid);
}


while(x < vector.size())
{
    pthread_join();
    x++;
}

Если вы не хотите зацикливаться на соединениях в своей главной записи, создайте поток, который ожидает их, передав вектор TID.

Если количество созданных объектов / потоков является безумным, используйте пул потоков. Или поставить счетчик в цикл создания, чтобы ограничить число потоков, которые могут быть созданы до объединения запущенных потоков.

...