| Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате |
| Форум программистов > C/C++: Общие вопросы > Помогите в организации многопоточности |
| Автор: xTr1m 2.10.2013, 08:27 | ||
День добрый, есть такая задача. Я запускаю один поток, который заполняет некий список. Также я запускаю несколько потоков (функция-обработчик у них одна), которые из этого списка значения тягают. Самой простой вариант этих двух функций выглядит как-то так
Но, как я понимаю, тут есть проблема (может и не одна), которая заключается в том, что потоки, которые "обрабатывают" значения из главного списка, работают постоянно. То есть бесконечный цикл, постоянно проверяет кол-во элементов на > 0. А ведь это процессорное время и т.п. Тогда я подумал сделать какое-нибудь событие. Типа поток, который AddToList, добавляет в список элемент и сигнализирует, "мол в списке что-то появилось, давайте, обрабатывайте". Но тогда в каком месте нужно ставить событие в состояние "список пуст, больше брать нельзя". Что мне приходит на ум: 1. добавить это в функцию GetFromToList, но ведь это просто обработчик, зачем ему проверять пуст ли список и создавать какое-то событие, к тому же его тоже нужно блокировать мьютексом, что не было одновременно можно-нельзя. 2. можно завести еще один поток, который бы синхронизировал этот момент. Но это тоже какой-то огород получается. 3. может есть другое решение? Спасибо. |
| Автор: cppGhost 2.10.2013, 18:58 |
| Мммм, то ли я плохо сформулировал вопрос, то ли это большой секрет =)) Переформулирую на каком-нибудь примере: на сервер поступают запросы (это первый поток). на сервере запущено несколько потоков, которые обрабатывают, поступившие запросы. Список запросов представляет собой вектор. Как мне кажется, неправильно делать так, чтобы эти потоки раз в N-секунд смотрели не пуст ли вектор запросов. Нужно этим потокам как то сообщать, что вектор уже не пуст. И так же нужно как то сообщать им, что запросы все обработаны и нужно подождать. Ну как то так. У меня не такая задача. У меня задача понять, как это грамотно реализуют. |
| Автор: xvr 3.10.2013, 12:42 |
Грамотно это реализуют применением примитивов синхронизации, например семафором и/или событий. Например семафор (который со счетчиком) считает количество элеменов в списке. При помещении в список семафор увеличивают на 1, при попытке чтения - ждут на нем. Если в списке есть элементы - ожидание завершается и счетчик автоматически уменьшается. Если же элементов нет, то поток чтения блокируется, пока элементы не появятся |
| Автор: xTr1m 10.10.2013, 13:48 | ||
Почитал еще литературу на эту тему и нашел то, что нужно в новом стандарте. Выглядит примерно так:
то есть в потоке, который считывает значения будет захвачен мьютекс, проверен результат IsListEmpty, если true то функция GetFromList пойдет дальше. Если false, то мьютекс освобождается и дальше идет ожидание вызова notify_one() как сигнала к еще одной проверке. Понятно, что гуру и так все знали =)) , но расписал я тут, чтобы самому еще раз это осмыслить, да и может кому то пригодится. xvr, за идею тоже спасибо, но с событиями наверное будет лучше. А может нет, тогда просьба рассказать почему?) |
| Автор: akizelokro 10.10.2013, 13:55 |
| Надо смотреть, как работают при многопоточности контейнеры стандартной библиотеки. Что-то такое читал, что то ли в стандарте контейнеры не safe-threaded, а в Intel Threading Blocks проблема решена. Но надо уточнять. |
| Автор: bsa 10.10.2013, 14:10 |
| STL контейнеры не реализуют потокобезопасность. |
| Автор: akizelokro 10.10.2013, 16:10 |
Я ставил tbb месяца 4 назад чтоли, мне надо было под винду портировать одну из замороченных библиотек на основе компьютерного зрения. Там использовался этот tbb и в нем как-то потокобезопасность для элементарных контейнеров, кажется, была реализована (интеловская кроссплатформенная библиотека для потоков). Так, на всякий случай информация |
| Автор: akizelokro 10.10.2013, 16:36 | ||
Вообще, по теме самого вопроса, более правильно, думаю, если не использовать потокобезопасную версию с этим tbb, то желательно бы "делать остановку" (mutex, auto, семафор, критическую секцию) полностью
для всего логического фрагмента. Потому что в общем случае контейнер может от момента определения числа элементов в векторе до момента вставки изменяться сколь угодно произвольно. А остальное, кажется, по идее должно быть правильным. Если использовать потокобезопасную версию tbb, то там нужно вспоминать, как такие вещи реализуются. Если интересна загрузка различных потоков с различными приоритетами выполнения, то это, по-моему, тоже делается. |
| Автор: bsa 11.10.2013, 00:01 |
| akizelokro, в спецификации STL указано, что контейнеры потокобезопасны для чтения, но для записи нет. То, что кто-то сделал их безопасные версии, я не сомневаюсь. Но когда ты используешь что-то нестандартное, то ты знаешь зачем и что это дает. |
| Автор: akizelokro 11.10.2013, 07:09 |
| Энто, конечно, да. Тут даже если пофлудить немного ненавязчиво захочется, много не попишешь. Поэтому внушает |
| Автор: xvr 11.10.2013, 11:40 |
Рассмотрим такой сценарий -
Не будет. Пример: Конфигурация потоков -
Сценарий исполнения -
|
| Автор: xTr1m 11.10.2013, 19:25 | ||
xvr, пример я брал из книги. попробовать это на компе пока не было времени, но что я понял из описания. В строке 28 захватывается мьютекс, в строке 29 проверяется возвращаемое значение CThreadsDlg::IsListEmpty. Если оно false, то мьютекс освобождается, а сам поток ожидает вызова notify_one для m_condition и deadlock'а вроде быть не должно.
так тут смысл в том, чтобы поток записи пишет сам по себе, независимо от потоков чтения. ждать их он не должен. |