I've written a Windows program in C++ which at times uses two threads: one background thread for performing time-consuming work; and another thread for managing the graphical interface. This way the program is still responsive to the user, which is needed to be able to abort a certain operation. The threads communicate via a shared bool
variable, which is set to true
when the GUI thread signals the worker thread to abort. Here is the code which implements this behaviour (I've stripped away irrelevant parts):
class ProgressBarDialog : protected Dialog {
/**
* This points to the variable which the worker thread reads to check if it
* should abort or not.
*/
bool volatile* threadParameterAbort_;
...
BOOL CALLBACK ProgressBarDialog::DialogProc( HWND dialog, UINT message,
WPARAM wParam, LPARAM lParam ) {
switch( message ) {
case WM_COMMAND :
switch ( LOWORD( wParam ) ) {
...
case IDCANCEL :
case IDC_BUTTON_CANCEL :
switch ( progressMode_ ) {
if ( confirmAbort() ) {
// This causes the worker thread to be aborted
*threadParameterAbort_ = true;
}
break;
}
return TRUE;
}
}
return FALSE;
}
...
};
class CsvFileHandler {
/**
* This points to the variable which is set by the GUI thread when this
* thread should abort its execution.
*/
bool volatile* threadParamAbort_;
...
ParseResult parseFile( ItemList* list ) {
ParseResult result;
...
while ( readLine( &line ) ) {
if ( ( threadParamAbort_ != NULL ) && *threadParamAbort_ ) {
break;
}
...
}
return result;
}
...
};
threadParameterAbort_
in both threads point to a bool
variable declared in a structure which is passed to the worker thread upon creation. It is declared as
bool volatile abortExecution_;
My question is: do I need to use volatile
here, and is the code above sufficient to ensure that the program is thread-safe? The way I've reasoned for justifying the use of volatile
here (see this question for background) is that it will:
prevent the reading of *threadParameterAbort_
to use the cache and instead get the value from memory, and
prevent the compiler from removing the if
clause in the worker thread due to optimization.
(The following paragraph is only concerned with the thread-safety of the program as such and does not, I repeat, does not involve claiming that volatile
in any way provides any means of ensuring thread-safety.) As far as I can tell, it should be thread-safe as setting of a bool
variable should in most, if not all, architectures be an atomic operation. But I could be wrong. And I'm also worried about if the compiler may reorder instructions such as to break thread-safety. But better be safe (no pun intended) than sorry.
EDIT:
A minor mistake in my wording made the question appear as if I was asking if volatile
is enough to ensure thread-safety. This was not my intent -- volatile
does indeed not ensure thread-safety in any way -- but what I meant to ask was if the code provided above exhibit the correct behaviour to ensure that the program is thread-safe.
Volatile is used in C programming when we need to go and read the value stored by the pointer at the address pointed by the pointer. If you need to change anything in your code that is out of compiler reach you can use this volatile keyword before the variable for which you want to change the value.
Volatile keyword is used to modify the value of a variable by different threads. It is also used to make classes thread safe. It means that multiple threads can use a method and instance of the classes at the same time without any problem. The volatile keyword can be used either with primitive type or objects.
C's volatile keyword is a qualifier that is applied to a variable when it is declared. It tells the compiler that the value of the variable may change at any time--without any action being taken by the code the compiler finds nearby.
Can we write multithreading programs in C? Unlike Java, multithreading is not supported by the language standard.
You should not depend on volatile to guarantee thread safety, this is because even though the compiler will guarantee that the the variable is always read from memory (and not a register cache), in multi-processor environments a memory barrier will also be required.
Rather use the correct lock around the shared memory. Locks like a Critical Section are often extremely lightweight and in a case of no contention will probably be all implemented userside. They will also contain the necessary memory barriers.
Volatile should only be used for memory mapped IO where multiple reads may return different values. Similarly for memory mapped writes.
Wikipedia says it pretty well.
In C, and consequently C++, the volatile keyword was intended to allow access to memory mapped devices allow uses of variables between setjmp allow uses of sig_atomic_t variables in signal handlers
Operations on volatile variables are not atomic nor establish a proper happens-before relationship for threading. This is according to the relevant standards (C, C++, POSIX, WIN32), and this is the matter of fact for the vast majority of current implementations. The volatile keyword is basically worthless as a portable threading construct.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With