0 / 0 / 0
Регистрация: 17.07.2020
Сообщений: 19
1

Передача в поток указателя на объект через std::cref

30.07.2020, 12:30. Показов 4993. Ответов 10

Author24 — интернет-сервис помощи студентам
Всем доброго времени суток!
Столкнулся с непонятным, для меня, поведением передаваемого в поток параметра:
Имеется некая структура данных с заранее заданным именем m_sName:
C++
1
2
3
4
5
struct StructDsc {
  std::string m_sName;
  std::thread *m_pThread={nullptr};
//Еще что-то
};
И ряд указателей на нее, объединенных в контейнер типа std::map
C++
1
std::map<std::string, void*> StructList;
Для каждого экземпляра, вышеописанной структуры необходимо создать поток, который будет циклически работать с переданным указателем
C++
1
2
3
4
5
6
for( auto iter = StructList.begin() ; iter != StructList.end() ; iter++ )
{
  auto Obj = static_cast<StructDsc*>(iter->second);
  std::cout << "Create thread:" << pObj->m_sName << endl;
  Obj ->m_pThread = new std::thread( MyThread, std::cref(Obj) );
}
Сама функция потока выглядит следующим образом
C++
1
2
3
4
5
6
7
8
9
void MyThread ( StructDsc * pObj )
{
  //Тут просто выводим имя объекта pObj
  std::cout << "Start thread:" << pObj->m_sName << endl;
  for(;;)
  {
    //Что-то делаем с данными объекта pObj …
  }
}
Проблема в том, что периодически в поток MyThread попадают одни и теже экземпляры структуры StructDsc, хотя в списке StructList они уникальны. Тоесть вывод может быть приблизительно такой:
Код
Create thread: name1
Start thread: name1
Create thread: name2
Create thread: name3
Start thread: name3
Start thread: name3
Create thread: name4
Start thread: name4
...
Почему такое происходит, может кто-нибудь объяснит?
Всем заранее спасибо!
0
Лучшие ответы (1)
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
30.07.2020, 12:30
Ответы с готовыми решениями:

Передача указателя на объект класса через this
Всем участникам форума горячий ПРИВЕТ! Вопрос такой. cl.h class cl { public: void cl(); ...

В чем отличия между std::cref() и std::bind()?
В документации не понял, что делает bind() ? И чем отличается cref() от операции взятия адреса? ...

Передача в поток указателя на контекст устройства
Здравствуйте! С потоками раньше дела не имел вовсе, но случилась необходимость освоить. Нужно...

Как создать объект оператором new, если имеется только тип указателя на объект, переданный через шаблон?
Есть шаблон. Точно известно, что его параметр Т это указатель. Как с помощью new создать переменную...

10
6091 / 3449 / 1402
Регистрация: 07.02.2019
Сообщений: 8,768
30.07.2020, 12:46 2
Obj - это локальный объект, а вы передаёте ссылку на него.
Он умрет сразу после очередной итерации цикла.
Зачем? Указатель можно передавать по значению.
1
0 / 0 / 0
Регистрация: 17.07.2020
Сообщений: 19
30.07.2020, 12:53  [ТС] 3
zayats80888
Цитата Сообщение от zayats80888 Посмотреть сообщение
Obj - это локальный объект, а вы передаёте ссылку на него.
Obj - это указатель на StructDsc, почему он должен умереть?
Цитата Сообщение от zayats80888 Посмотреть сообщение
Указатель можно передавать по значению.
Вот так:
C++
1
Obj ->m_pThread = new std::thread( MyThread, Obj );
или так?
C++
1
Obj ->m_pThread = new std::thread( MyThread, std::ref(Obj) );
0
6091 / 3449 / 1402
Регистрация: 07.02.2019
Сообщений: 8,768
30.07.2020, 12:59 4
Лучший ответ Сообщение было отмечено Иван_79 как решение

Решение

Цитата Сообщение от Иван_79 Посмотреть сообщение
Obj - это указатель на StructDsc, почему он должен умереть?
Не важно, указатель или что другое, время его жизни ограничено скоупом тела цикла.
А в поток фактически вы передаёте не указатель на StructDsc , а адрес(ссылку) локального указателя на StructDsc.
Цитата Сообщение от Иван_79 Посмотреть сообщение
или так?
нет, первый вариант.
1
0 / 0 / 0
Регистрация: 17.07.2020
Сообщений: 19
30.07.2020, 13:03  [ТС] 5
Тогда в каком случае следует использовать
C++
1
std::ref
или
C++
1
std::cref
при передаче параметров в поток?
0
6091 / 3449 / 1402
Регистрация: 07.02.2019
Сообщений: 8,768
30.07.2020, 13:12 6
Цитата Сообщение от Иван_79 Посмотреть сообщение
в каком случае следует использовать
Когда функция потока принимает параметр по ссылке(должна работать с объектом, а не его копией) и, что важно(!),
объект должен быть жив на момент обращения к нему по ссылке(проще говоря, он должен умирать не раньше, чем объект потока, хотя это не обязательно, достаточно что бы последнее обращение к нему по ссылке происходило межпоточно-раньше(inter-thread happens-before) вызова его деструктора)
0
0 / 0 / 0
Регистрация: 17.07.2020
Сообщений: 19
30.07.2020, 13:36  [ТС] 7
Тогда если я хочу передать в поток, ну скажем его номер, и делаю это так
C++
1
2
3
4
int iNum = 1;
auto pTh1 = new std::thread( Thread, std::ref(iNum) );
iNum = 2;
auto pTh2 = new std::thread( Thread, std::ref(iNum) );
как правильней это сделать, через std::ref или просто: std::thread( Thread, iNum );
0
6091 / 3449 / 1402
Регистрация: 07.02.2019
Сообщений: 8,768
30.07.2020, 14:13 8
Цитата Сообщение от Иван_79 Посмотреть сообщение
как правильней это сделать
Если функция потока не должна изменять значение iNum(хотя она может изменять копию этого значения в своих целях, ей главное получить начальное значение)
или не должна видеть изменения значения iNum из других потоков,
то разумно передавать копию.

---

Не по теме:

Иван_79, прежде чем осваивать многопоточность, советую освоить базовый курс С++, а то у вас плохое понимание базовых вещей, таких как время жизни объекта, что такое указатели и ссылки, как передаются параметры в функции и т.д.

1
18828 / 9831 / 2403
Регистрация: 30.01.2014
Сообщений: 17,267
30.07.2020, 14:14 9
Цитата Сообщение от Иван_79 Посмотреть сообщение
Тогда если я хочу передать в поток, ну скажем его номер
Тут для использования cref у вас главное, чтобы iNum был жив столько, сколько требуется потоку для работы с ним.

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

Вы должны рассчитывать где и сколько живут ваши объекты.
1
0 / 0 / 0
Регистрация: 17.07.2020
Сообщений: 19
30.07.2020, 14:22  [ТС] 10
DrOffset
Цитата Сообщение от DrOffset Посмотреть сообщение
Тут у вас главное, чтобы iNum был жив столько, сколько требуется потоку для работы с ним.
Но в поток я передаю значение
C++
1
std::thread( Thread, iNum );
а не ссылку.
Цитата Сообщение от DrOffset Посмотреть сообщение
время жизни которой ограничено ее scope-ом.
Почему значение, в этом случае, будет ограничено временем временем жизни iNum?
Реализация потока
C++
1
2
3
4
5
6
7
void Thread( const int number )
{
  for(;;)
  {
    //Разьве здесь number будет зависеть от времени жизни iNum ???
  }
}
0
18828 / 9831 / 2403
Регистрация: 30.01.2014
Сообщений: 17,267
30.07.2020, 14:38 11
Иван_79, речь о использовании cref/ref, т.е. о ссылках.

Добавлено через 4 минуты
Цитата Сообщение от Иван_79 Посмотреть сообщение
Но в поток я передаю значение
Вы в своем примере написали следующее:
Цитата Сообщение от Иван_79 Посмотреть сообщение
C++
1
2
3
4
int iNum = 1;
auto pTh1 = new std::thread( Thread, std::ref(iNum) );
iNum = 2;
auto pTh2 = new std::thread( Thread, std::ref(iNum) );
именно этот пример я и комментирую.
1
30.07.2020, 14:38
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
30.07.2020, 14:38
Помогаю со студенческими работами здесь

Передача указателя на объект как параметра функции
Доброго времени суток уважаемые форумчане. Я прошу сразу простить меня за быть может не совсем...

Передача в метод по перегрузке оператора указателя на объект
У нас есть массив указателей на базовый класс STRING **ptr = new STRING* ; for (int i = 0;...

Передача указателя в класс на объект другого класса
Не могу передать ссылку на объект класса в другой класс main.cpp #include &lt;iostream&gt;...

Реализация передачи указателя на объект через очередь сообщений
У меня есть объект(Variant). Мне надо , с помошью очереди сообщений , передать указатель на него из...

Передача указателя на класс через SendMessage
Приветствую. Не получается передать указатель на экземпляр класса через SendMessage. Передаю...

Стандартный поток и STL (std::copy to std::cout)
#include &lt;iostream&gt; #include &lt;sstream&gt; #include &lt;algorithm&gt; #include &lt;functional&gt; #include...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
11
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2024, CyberForum.ru