move семантика и многопоточность
От: sanx  
Дата: 12.10.15 23:42
Оценка:
С точки зрения правильного C++, при проектировании класса нужно относиться к move семантике как к деструктору, в том смысле что старый объект разрушается, правильно ли? Например такая ситуация: класс реализует долгую операцию, и содержит поля используемые в этой операции. Как быть если кто-то захочет сделать move из другого потока? Можно сделать некий lock/unlock и отслеживать перед move, но как сделать поддержку move «правильно»? Запретить move семантику для класса? Кидать исключение? Понимаю что я не совсем понимаю move :)
move
Re: move семантика и многопоточность
От: watchmaker  
Дата: 13.10.15 00:34
Оценка: +3
Здравствуйте, sanx, Вы писали:

S>С точки зрения правильного C++, при проектировании класса нужно относиться к move семантике как к деструктору, в том смысле что старый объект разрушается, правильно ли?

Нет. Посмотри, например, что происходит в стандартной библиотеке — практически все объекты просто оказываются в "valid but unspecified state". Это совсем не разрушение. Так вектор, из которого произошло перемещение, можно вполне продолжать использовать дальше (хотя, почти всегда сначала следует вызвать метод .clear, чтобы перевести его в "specified state"). И если вектор после move обладал каким-то зарезервированным участком памяти, то он так и останется им владеть — соответствующая память будет освобождена только в деструкторе.

Соответственно, нормальная практика — это ожидать, что объекты после перемещения останутся в "valid but unspecified state". И самому писать код так, чтобы это постусловие для классов выполнялось.


S>Например такая ситуация: класс реализует долгую операцию, и содержит поля используемые в этой операции. Как быть если кто-то захочет сделать move из другого потока?

А если кто-то захочет скопировать объект? А если кто-то захочет вызвать у него какой-нибудь метод? Почему именно перемещение рассматриваешь так особенно, а не более частые ситуации?
К этим проблемам move ничего не добавляет нового.


S>Запретить move семантику для класса? Кидать исключение?

Предлагаю запретить вызывать методы класса, кидать исключении при копировании. А для move можно и посложнее прикол придумать

А если серьёзно, то делай как и у других методов сделано. Если класс объявлен не thread-safe, то ничего. Если копирование происходит под мьютексом, то и перемещение пусть будет под ним же. Кидаешь исключения при обнаружении доступа из разных потоков? Ну тогда кидай их отовсюду.
Re: Valid state
От: Qbit86 Кипр
Дата: 13.10.15 06:36
Оценка:
Здравствуйте, sanx, Вы писали:

S>С точки зрения правильного C++, при проектировании класса нужно относиться к move семантике как к деструктору, в том смысле что старый объект разрушается, правильно ли?


«A move operation should move and leave its source in valid state... Ideally, that moved-from should be the default value of the type... The standard library assumes that it it possible to assign to a moved-from object. Always leave the moved-from object in some (necessarily specified) valid state.»
https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Rc-move-semantic
Глаза у меня добрые, но рубашка — смирительная!
Re: move семантика и многопоточность
От: Кодт Россия  
Дата: 13.10.15 13:58
Оценка: 2 (1)
Здравствуйте, sanx, Вы писали:

S>С точки зрения правильного C++, при проектировании класса нужно относиться к move семантике как к деструктору, в том смысле что старый объект разрушается, правильно ли? Например такая ситуация: класс реализует долгую операцию, и содержит поля используемые в этой операции. Как быть если кто-то захочет сделать move из другого потока? Можно сделать некий lock/unlock и отслеживать перед move, но как сделать поддержку move «правильно»? Запретить move семантику для класса? Кидать исключение? Понимаю что я не совсем понимаю move


Проще относиться к перемещению как к частному случаю обычного присваивания. Присваивается всякая ерунда — обычно, либо пустое значение, либо то же самое, либо значение приёмника.
Нужно ли лочить объект во время присваивания? (Риторический вопрос).

Если класс сам реализует долгую операцию, то многопоточный доступ к нему надо проектировать в расчёте на задержки, блокировки, отлупы мгновенные и по таймауту.
Использовать при этом примитивный синтаксис и примитивную семантику, т.е. присваивание, копирование, перемещение, — которые подразумевают атомарное и быстрое действие, — это вводить себя в заблуждение.
Т.е. вместо
Foo f;
f.start_doing_long_async_operation();
f = Foo(123);

лучше будет что-то этакое
f.start_doing_long_async_operation();
f.try_assign_with_timeout(INFINITY, 123);


Единственное исключение из этого — когда пишется одноразовая и полностью синхронная программа. Где неограниченное ожидание в порядке вещей, а если зависнет из-за неудачно сложившихся звёзд — то и наплевать, перезапустим.
Перекуём баги на фичи!
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.