2

I'm working pre C++11 otherwise I'd just use include thread and atomic variables to fulfill my needs, however, can't do that. Got a class that when instanced starts several threads. Within a thread launched function I've got something like:

void ThisClass::ThisThread()
{
    while (runThisThread)
    {
        // doing stuff
    }
}

And another function that would be:

void ThisClass::StopThisThread()
{
    runThisThread = false; // 'runThisThread' variable is 'volatile bool'
}

A thread will be chewing through a buffer based on indexes assigned from another thread. So one thread would assign a value that another thread would never do anything but read that value. My plan was to use more volatile memory to assign those index values. However, this question suggests I'm using volatile memory incorrectly When to use volatile with multi threading?. What is the correct way pre-C++11 to handle memory in a multithreaded class like this? Keep in mind I am not allowing more than one thread to assign a single variable while each thread may read that variable.

EDIT: Forgot to include that this is a Windows program with no need for cross platforming. I'm using afxwin.h AfxBeginThread() for my threading.

Community
  • 1
  • 1
Brad B.
  • 316
  • 2
  • 13
  • 1
    You want either atomic variables or mutex's. I leave it as an exercise to the OP to research them. – UKMonkey Oct 19 '16 at 14:08
  • 5
    Use a mutex from the threading API you use. – πάντα ῥεῖ Oct 19 '16 at 14:08
  • The API you use should have some tutorial somewhere. mutex or synchronisation mechanisms should be mentioned there somewhere – Hayt Oct 19 '16 at 14:11
  • 2
    Since threads are not part of C++ 98, you must be using a library for that, like pthreads. This library will also provide facilities for synchronization and memory barriers. Use those. – Peter - Reinstate Monica Oct 19 '16 at 14:13
  • Microsoft Extends `volatile` to accomodate atomic-like sharing of values among threads, so declaring `runThisThread` `volatile` should do what you want, as long as a `bool` value can be "copied in one hardware instruction", and you are compiling with the `/volatile:ms` option (the default for non-ARM architechtures). See: https://msdn.microsoft.com/en-us/library/12a04hfd.aspx – Christopher Oicles Oct 20 '16 at 01:27

2 Answers2

3

This scenario is best solved using a manual reset event object (or the corresponding CEvent MFC wrapper). When you want to terminate the thread, you simply signal the event. The thread loop should evaluate the event state:

while( ::WaitForSingleObject( hEvent, 0 ) == WAIT_TIMEOUT ) {
    // doing stuff
}

Or as an MFC version:

while( !myEvent.Lock( 0 ) ) {
    // doing stuff
}
IInspectable
  • 46,945
  • 8
  • 85
  • 181
  • That works for the stopping of the threads. Yet it doesn't address the second part of the question. "A thread will be chewing through a buffer based on indexes assigned from another thread. So one thread would assign a value that another thread would never do anything but read that value. My plan was to use more volatile memory to assign those index values. However, this question suggests I'm using volatile memory incorrectly When to use volatile with multi threading?." There still needs to be a int variable that one thread assigns and another thread reads. – Brad B. Oct 20 '16 at 13:01
  • @BradB.: That wasn't clear to me, when I read the question. For a non-portable solution you could use a `volatile int` and the compiler switch `/volatile:ms` (the default for non-ARM architectures). If you want something that's portable across compilers, you can pick any [synchronization](https://msdn.microsoft.com/en-us/library/windows/desktop/ms686353.aspx) facility (like SRWLs, or the InterlockedXyz APIs). – IInspectable Oct 20 '16 at 13:07
  • sorry for the delay. I've selected your answer as the correct one. I wanted to add for any future person that looks at this question. Anything Windows 7 or earlier is a non-ARM architecture. Microsoft did not support it pre-Windows 7 so if you're on one of those machines you are safe to assume /volatile:ms is the selected option. – Brad B. Nov 01 '16 at 13:20
0

The most efficient way to handle this while leaving the code as written is to use the interlocked functions:

volatile DWORD runThisThread;

void ThisClass::ThisThread()
{
    while (InterlockedCompareExchange(&runThisThread, 0, 0))
    {
        // doing stuff
    }
}

void ThisClass::StopThisThread()
{
    InterlockedExchange(&runThisThread, false);
}

This is significantly faster than using a critical section to protect the variable or replacing the variable with an event object.

(However, if your loop needs to idle, for example while waiting for more work, then you should be using an event object to avoid busy waiting.)

Harry Johnston
  • 35,639
  • 6
  • 68
  • 158