LINUX.ORG.RU

Доступ к std::list из разных потоков


0

1

Как следует обращаться с std::list из разных потоков?

Вот например ситуация. В одном потоке начинается перебор элементов итератором, в середине списка выполнение останавливается (например из-за блокировки мьютексом). Следом в другом потоке происходит добавление элемента к этому списку. После этого не нарушится ли продолжение перебора в первом потоке тем же итератором?

★★★

Ответ на: комментарий от anonymous

Ну это наверно частный случай реализации, а как оно в общем должно быть? У меня код должен компилироваться и под Linux и под оффтопик.

normann ★★★
() автор топика
Ответ на: комментарий от anonymous

Но мы рискуем прочитать не все данные. Это, скорее всего, будет плохо.
Но если вопрос ТСа понимать как «не случится ли сегфолт», то согласен.

Stahl ★★☆
()
Ответ на: комментарий от normann

Ну это наверно частный случай реализации

а как ты себе по-другому представляешь итерацию по списку?

anonymous
()

А зачем ты модифицируешь список, когда итерируешь? Пройдись по нему алгоритмом, а потом позволяй модифицировать.

И у тебя там только добавление? Довавление в конец, или где-то в середину? Что будет если удалять?

nanoolinux ★★★★
()
Последнее исправление: nanoolinux (всего исправлений: 2)
Ответ на: комментарий от Stahl

Но мы рискуем прочитать не все данные. Это, скорее всего, будет плохо.

Ну, костылями и подпорками это можно обойти, но твой первый ответ лучше

Stil ★★★★★
()
Ответ на: комментарий от anonymous

Все что я представлю будет только теорией.

normann ★★★
() автор топика
Ответ на: комментарий от nanoolinux

Модификация списка во время итерации нужна для того чтобы модифицирующий поток не простаивал в ожидании когда поток чтец обработает весь список.

При добавлении элемент всегда добавляется в начало списка. Так что читающий поток по любому его уже не зацепит новый элемент, но так и нужно.

Главная задача в том чтобы, независимо от процесса модификации, читающий поток смог перебрать без потерь все те элементы списка которые были там до добавления кроме тех что были удалены.

normann ★★★
() автор топика

Согласно стандарту ты не можешь вызывать из разных потоков методы, разве что они оба константные (С++11). Так что или мьотекс, или не std::list. Все остальное - дикий быдлокод.

Pavval ★★★★★
()
Ответ на: комментарий от Stahl

Но мы рискуем прочитать не все данные.

Что значит «не все» в случае, когда что-то постоянно добавляется-удаляется?
Если ставится требование сохранить конкретное состояние списка - то это уже отдельная задача и решать её можно тоже разными методами.

invy ★★★★★
()
Ответ на: комментарий от Pavval

Согласно стандарту ты не можешь вызывать из разных потоков методы

Я что-то не понял, можно поподробнее?

normann ★★★
() автор топика
Ответ на: комментарий от normann

Чтобы вызывать из разных потоков методы любого класса любой библиотеки, ты должен знать, что эти методы потокобезопасны. С++11 гарантирует потокобезопасность всех const методов STL, но не более. Потому читать std::list с одного потока, а писать с другого без какой-либо синхронизации ты не можешь.

А если ты будешь полагаться на особенности какой-либо конкретной реализации STL, то имя тебе - быдлокодер.

Pavval ★★★★★
()
Ответ на: комментарий от normann

Модификация списка во время итерации нужна для того чтобы модифицирующий поток не простаивал в ожидании когда поток чтец обработает весь список.

Сколько времени требуется для обработки списка? Какие рт требования для модифицирующего потока?

nanoolinux ★★★★
()
Ответ на: комментарий от normann

По какому делу? Ответил по теме треда. Если ты хочешь себе рассказать, что ничего не произойдет - вперед писать говно. Но зачем тогда было создавать тред?

Pavval ★★★★★
()
Ответ на: комментарий от nanoolinux

Поток принимающий входящие через сокет подключения по accept() должен добавлять данные в список при каждом подключении. Если он будет стоять и ждать пока читающий поток пройдет весь список то процесс может стать недоступным для процессов пытающихся подключиться. Размер списка не ограничен.

normann ★★★
() автор топика
Последнее исправление: normann (всего исправлений: 1)
Ответ на: комментарий от Pavval

А если ты будешь полагаться на особенности какой-либо конкретной реализации STL, то имя тебе - быдлокодер

включить именно эту конкретную реализацию в исходники

anonymous
()

Вопрос остается открытым: может ли стать недействительным итератор если в начало списка были добавлены данные?

normann ★★★
() автор топика

Быдлокодеры не могут в многопоточность.

ТСу надо почитать что-нибудь фундаментальное по сабжу.

anonymous
()
Ответ на: комментарий от normann

Addition, removal and moving the elements within the list or across several lists does not invalidate the iterators or references. An iterator is invalidated only when the corresponding element is deleted.

sjinks ★★★
()
Ответ на: комментарий от sjinks

Addition, removal and moving the elements within the list or across several lists does not invalidate the iterators or references. An iterator is invalidated only when the corresponding element is deleted.

Ох спасибочки!

А откуда инфа?

normann ★★★
() автор топика
Ответ на: комментарий от anonymous

включить именно эту конкретную реализацию в исходники

Именно. Тогда она не будет из STL, а будет твоей личной правильной реализацией.

Pavval ★★★★★
()
Ответ на: комментарий от normann

Я не случайно спросил тебя о времени обработки списка. Обмен данными по сети для процессора - почти вечность.

то процесс может стать недоступным для процессов пытающихся подключиться.

man listen, про backlog почитай. Я тебя специально спросил про время прохода по списку - это ключевой вопрос, который определяет стратегию решения твоей задачи.

nanoolinux ★★★★
()
Ответ на: комментарий от nanoolinux

man listen, про backlog почитай

backlog не бесконечен. Вот цитата из мана (правда пусть не смущает что на русском):

Если сокет имеет тип AF_INET, а аргумент backlog больше, чем константа SOMAXCONN (128 в Linux 2.0 & 2.2), то он незаметно обрезается до SOMAXCONN. Не полагайтесь на это значение в портабельных приложениях, потому что BSD и некоторые её потомки ограничивают размер очереди до 5.

время прохода по списку

Это неизветсно т.к. (как уже писал) размер списка не ограничен, может быть любой.

normann ★★★
() автор топика
Ответ на: комментарий от normann

Вопрос остается открытым: может ли стать недействительным итератор если в начало списка были добавлены данные?

Вопрос поставлен неправильно.

Правильный вопрос: с чего ты взял, что другой поток вообще получит корректный итератор, а не погоду на Марсе?

Допустим, у тебя есть поток 1, который добавляет элемент в голову списка:

ListNode * node = new ListNode();
node->data = somedata();
node->next = this->head;
this->head = node;

В это время поток 2 начинает последовательно читать список.

Казалось бы, в чем подвох? Поток 2 прочитает из this->head либо старое значение, либо новое, но в обоих случаях список будет корректен.

Щажже! И не мечтай.

C++ не гарантируют, что другой поток увидит изменения ОЗУ именно в том порядке, в каком они были применены в первом потоке. И имя этому беспределу — weak memory consistency аппаратной платформы.

Для потока 2 всё может выглядет так, будто СНАЧАЛА выполнено присваивание this->head = node, а уже ПОСЛЕ в node->data и node->next были записаны значения. И просто читая this->head->next ты можешь получить дырку от бублика вместо корректного указателя.

Вывод:

Изучай многопоточность, изучай, как работает современное железо, изучай, какие гарантии даёт твой ЯП и какие в нём есть средства эти гарантии усилить (memory barriers и всё такое).

anonymous
()
Ответ на: комментарий от anonymous

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

normann ★★★
() автор топика
Ответ на: комментарий от nanoolinux

Там объекты олицетворяющие клиентские подключения подписанные на раздачу некоторых сигналов. Кто в списке тот подписан. Поток читатель раздает сигнал по списку.

normann ★★★
() автор топика
Последнее исправление: normann (всего исправлений: 1)
Ответ на: комментарий от aol

Да, сказали уже. Но все равно спасибо.

normann ★★★
() автор топика
Ответ на: комментарий от normann

Может как-то так: есть список входящих подключений, защищенный мьютексом - его заполняет поток Acceptor, второй - список «зарегистрированных» клиентов (мьютексом он не защищается) обрабатывает поток Sender. На каждой итерации Sender проверяет размер списка Acceptor'a и если он не равен нулю, то запирает защищающий его мьютекс, перемещает содержимое в свой список и мьютекс освобождает, далее начинает итерацию по своему списку. Таким образом поток Acceptor будет блокироваться лишь на короткий момент времени, необходимый для перемещения содержимого его списка. Такой вариант не подойдет?

NegatiV
()
Ответ на: комментарий от normann

При добавлении элемент всегда добавляется в начало списка. Так что читающий поток по любому его уже не зацепит новый элемент, но так и нужно.

За это убивать нужно (с)
Не стоит модифицировать список во время итерации. Это хрень полная, т.к. результат итерации будет неправильный.

vada ★★★★★
()
Ответ на: комментарий от NegatiV

Можно и так, можно и много как, но это уже наращение кода, но стоит прикинуть варианты и попроще. Я думал о таком варианте, но выяснив в этом треде специфику std::list мне кажется лучше sender'у не создавать свой список, а блокировать список лишь на момент выборки очередного элемента, и acceptor блокирует тем же мьютексом когда добавляет в список.

normann ★★★
() автор топика
Ответ на: комментарий от normann

В моем варианте кода немного больше нужно, а блокировок будет меньше.

NegatiV
()

https://www.google.ru/?q=lock-free+list

не благодари

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.