У меня есть полиморфный объект o
и две темы T1
а также T2
,
Деструктор o
Самый производный класс ожидает завершения T2
перед возвращением.
Это безопасно, чтобы позволить T1
удалять o
в то время как T2
вызывает некоторые виртуальные функции o
? (Я имею в виду, не используя взаимное исключение или любые другие механизмы синхронизации)
Я считаю, что это должно быть безопасно, если delete
разрешено изменять o
(как его указатель на vtable) еще до завершения первого вызванного деструктора. Это тот случай?
Во-первых, если вы можете избежать этого, он хрупок и подвержен ошибкам. Вы можете заставить это работать, но небольшие изменения в коде могут сломать это.
Если единственное, что делает блок в деструкторе полного объекта, — это ожидание завершения другого потока и предположения, что базовые объекты имеют виртуальные деструкторы или что весь объект уничтожен напрямую, затем это безопасный сделать это. Ни один из членов объекта не будет уничтожен до того, как тело деструктора завершится, и ни одна из них не является базой. Это означает, что ни один из подобъектов, используемых другим потоком, не будет уничтожен до его завершения (и позволит первому потоку завершить тело деструктора).
При этом, опять же, попробуйте изменить код.
Представьте себе сценарий, в котором ваш класс имеет несколько динамически распределенных членов,
T1
деструктор может освободить этих членов T2
все еще получает доступ к этим членам.
Это приведет к неопределенному поведению.
Пока вы можете гарантировать, что T1
деструктор и T2
Функции не работают с теми же членами, с которыми вы в безопасности, но если вы не можете гарантировать это, вам определенно нужна синхронизация.
Обратите внимание, что основная логика остается неизменной, никакие два потока не могут одновременно читать и записывать в одной и той же сущности, в противном случае вы сталкиваетесь с условиями гонки и проблемами синхронизации.
Как уже отмечалось, это рискованно, но вы можете предпринять некоторые шаги для снижения риска. Ответственность за синхронизацию потоков должна быть единственной ответственностью самого производного класса. В частности, этот наиболее производный класс НЕ должен иметь никаких виртуальных функций, кроме деструктора.
Этот дизайн означает, что T2 не может вызывать любую функцию в самом производном классе. В лучшем случае это может зависеть от членов базового класса o
, и они будут действительны до тех пор, пока не будет получен самый производный dtor — что будет после выхода из T2. Это означает, что следующая последовательность не переупорядочена, за исключением, возможно, шагов 2 и 3 (безвредно)
T2
может получить доступ к методам базового класса o
T1
Дтор o
введенT2
выходы.T1
Дтор o
возвращаетсяo
больше не может быть вызван.