2017-02-11 43 views
1

Говорят, летучее необходим для обработчика сигнала, например,летучее для обработчика сигнала и многопоточного

volatile int flag = 1; // volatile is needed here? 

void run() { 
    while(flag) { /* do someting... */ } 
} 

void signal_handler(int sig) { 
    flag = 0; 
} 

int main() { 
    signal(SIGINT, sig_handler); 
    run(); 
    // ... 
} 

Говорят, летучие часто не используются в многопоточной. Но как насчет аналогичного случая, подобного выше в многопоточности:

int flag = 1; // is volatile needed here? 

void thread_function() { 
    while(flag) { /* do someting... */ } 
} 

int main() { 
    // pthread_create() to create thread_function()... 
    sleep(10); // let thread_function run for 10 seconds 
    flag = 0; 
    // ... 
} 

Следует ли использовать в качестве замены ключевое слово volatile? Компилируются ли эти два случая одним и тем же способом?

+1

'volatile' недостаточно [обычно] для операций interthread (например, блокировки, индексов очереди звонков и т. Д.). Но здесь вы используете флаг «run». Только 1 автор ('main'). И в обоих случаях он должен быть «изменчивым». Во втором случае предположим, что функция func является 'while (flag) do_stuff();' '' '' '' '' '' '' '' '' '' '' 'мягкий способ сообщить потокам выйти из своих циклов, выполнить очистку и выйти. Вы можете объединить 1-й и 2-й и обработать обработчик сигнала 'флаг' для потоков. Многое для этого для потоков требует 'pthread_mutex_t' et. al. и т. д. –

ответ

1

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

Другими словами, всякий раз, когда событие «снаружи» может изменить значение переменной, вы должны рассмотреть возможность использования volatile («снаружи» - как в, вне соответствующего кодового блока).

В обоих примерах вы используете переменную как флаг, чтобы сигнализировать об изменении поведения. Этот флаг в обоих примерах управляется событиями «за пределами» цикла, который проверяет флаг. По этой причине оба примера требуют использования ключевого слова volatile.

Следует отметить, что volatileне обеспечивает безопасность потоков по ряду причин. Чтобы убедиться, что объект является потокобезопасным, операции чтения/записи должны быть защищены или атомарны.

+1

...и когда операции чтения/записи защищены или атомарны, вам больше не нужно 'volatile' –

+0

@ M.M. Хотя это правда, все же хорошей практикой является использование изменчивых операций с защищенными операциями. Хотя атомарные переменные C11 всегда считываются из памяти, а не из ЦП (при использовании 'atomic_load'), переменные, которые изменяются внутри блокировки,« подразумеваются »как неустойчивые ... но оптимизация может делать забавные вещи, когда данные считываются за пределами блокировки, например, в примерах OP. – Myst

6

Единственными нелокальными значениями, которые вы можете изменить из обработчика сигнала, являются значения типа volatile sig_atomic_t и атомных типов. В частности, запись на ваш volatile int разрешена , а не, и если ваш обработчик сигналов работает, вы имеете неопределенное поведение.

+2

Полезно, если слегка касательное, добавление: в то время как вам не разрешено очень много в контексте обработчика сигнала, вы можете 'write()' в дескриптор файла. Это часто самый полезный способ получить слово «событие» в вашей основной программной логике. Linux даже предлагает удобный API '' signalfd' (http://man7.org/linux/man-pages/man2/signalfd.2.html) для именно этого шаблона использования (т. Е. Вместо того, чтобы писать обработчики сигналов, которые явно пересылаются событие, зарегистрируйте 'signalfd', чтобы прочитать события из). – user268396

+0

Разрешено ли вам изменять атомные типы, отличные от 'sig_atomic_t'? Я думал, что это единственный. в противном случае, зачем это вообще нужно? – rici

+0

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