2013-07-12 2 views
2

Я работаю с потоками и у меня есть вопрос о том, как компиляторы могут оптимизировать следующий код:Когда переменные удаляются оптимизацией?

void MyClass::f(){ 
    Parent* p = this->m_parent; 
    this->m_done = true; 
    p->function(); 
} 

Это очень важно, что p (в стеке или в регистре) используется для вызова вместо this->m_parent. Поскольку m_done становится true, тогда this может быть удален из другого потока, если это произойдет, чтобы запустить его очистку (у меня были фактические сбои из-за этого), и в этом случае m_parent может содержать мусор, но стеки/регистры потоков будут неповрежденными.

Мое начальное тестирование на GCC/Linux показывает, что у меня нет состояния гонки, но я хотел бы знать, будет ли это так и в других компиляторах?

Это случай для gulpvolatile? Я посмотрел на What kinds of optimizations does 'volatile' prevent in C++? и Is 'volatile' needed in this multi-threaded C++ code?, но я не чувствовал, что любой из них применил мою проблему.

Я чувствую себя неловко, полагаясь на это, поскольку я не уверен, что здесь разрешен компилятор. Я вижу следующие случаи:

  • оптимизация No/Благодатная, указатель в this->m_parent хранятся в стеке/регистре, и это значение в дальнейшем используются для вызова function() это разыскивается поведение.
  • Компилятор удаляет p, но this->m_parent случайно представлен в регистре, и компилятор использует это для вызова function(), но он будет ненадежным между компиляторами. Это плохо, так как это может привести к перебору ошибок в другой версии платформы/компилятора.
  • Компилятор удаляет p и читает this->m_parent перед вызовом function(), это создает состояние гонки, которое у меня не может быть.

Может ли кто-нибудь пролить свет на то, что здесь разрешен компилятор?

Редактировать

Я забыл упомянуть, что this->m_done является std::atomic<bool>, и я использую C++ 11.

+0

Ключевое слово 'register' будет делать то, что я хочу, если компилятор будет уважать его ... –

+0

это не проблема волатильности/регистрации. Это проблема переупорядочения. Компилятору разрешено устанавливать m_done в true до того, как он загрузит m_parent. –

+0

Также «volatile» заставит считывать память с шины, что на самом деле не так (хотя это, вероятно, может быть использовано для решения моей проблемы). Я просто хочу сказать компилятору «Довольно, пожалуйста, сохраните это значение в регистре», потому что он скоро исчезнет, ​​и мне понадобится копия « –

ответ

2

Этот код будет работать отлично, как написано на C++ 11, если m_done is std::atomic<bool>. Считывания m_parent является секвенировал-перед тем на запись в m_done который будет синхронизировать с гипотетическим чтения из m_done в другом потоке, который секвенировал-перед тем гипотетической записью в m_parent. В совокупности это означает, что стандарт гарантирует, что этот поток прочитан m_parentпроисходит до записи другого потока.

+0

Спасибо, что меня продает! И я закончу это цитатой от Мак-Хаммера: «На-на-на-на не может коснуться этого!» (извините, что я должен был, я просто нашел это веселым, я знаю, я ужасный) –

+0

@EmilyB .: Обратите внимание на недостаток upvotes :) Я думаю, что вы были слишком быстры, чтобы принять ответ (и отклонить ответ Пита); Я склонен полагать, что это правильно, но у меня нет никаких ссылок на это. Возможно, вы захотите продолжить проверку ответов на всякий случай. – Mehrdad

+1

«Совершенно как написано» предназначалось только для привязки к памяти: код не имеет данных. – Casey

1

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

Реализации доступны в C++ 11 и boost.

+1

Собственно, 'this-> m_done' является' std :: atomic 'не является автоматическим барьером памяти? –

+0

согласно http://en.cppreference.com/w/cpp/atomic/atomic - да. См. Второй абзац. –

-1

Ум, какой-либо другой поток может удалить объект из-под вас, когда вы находитесь в одной из своих функций-членов? Неопределенное поведение. Легко и просто.

+0

. Поведение отлично определено, если я не касаюсь 'this' после того, как это произойдет, содержимое стека и регистра допустимо и четко определено. –

+0

@EmilyB. - Нет, это эвристика. Определение языка C++ не требует, чтобы это работало; это то, что означает «неопределенное поведение». –

+1

[править] – Mehrdad

Смежные вопросы