0

I'm seeing a lot of threads on this forum dealing with a question whether or not we need to use synchronization when accessing primitive data types from multiple threads: Question 1, question 2, question 3, question 4...

So I wrote a small test to verify this:

I ran it for over an hour on my CPU Intel(R) Core(TM) i7 CPU 860 @ 2.80GHz that runs with 4 physical cores:

#define MULTIPLIC_VAL 17

DWORD gdwSharedVal01 = MULTIPLIC_VAL;

DWORD WINAPI thread001(LPVOID lpParameter);

//Begin threads
for(int i = 0; i < 30; i++)
{
    DWORD dwThreadId;
    HANDLE hThread = ::CreateThread(NULL, 0, thread001, NULL, 0, &dwThreadId);
    if(hThread)
    {
        ::CloseHandle(hThread);
    }
    else
    {
        _tprintf(L"ERROR: CreateThread error %d\n", ::GetLastError());
    }
}

//Wait
getchar();


BOOL checkSharedValue()
{
    //RETURN:
    //      = TRUE if value is OK
    if((gdwSharedVal01 % MULTIPLIC_VAL) == 0)
    {
        return TRUE;
    }

    return FALSE;
}


DWORD WINAPI thread001(LPVOID lpParameter)
{
    srand((UINT)time(NULL));

    DWORD dwThreadID = ::GetCurrentThreadId();
    _tprintf(L"Thread %d began...\n", dwThreadID);

    for(;;)
    {
        //Set value
        DWORD v = rand();
        v <<= 16;
        v ^= rand();

        v = v / MULTIPLIC_VAL;
        gdwSharedVal01 = v * MULTIPLIC_VAL;

        //Check value
        if(!checkSharedValue())
        {
            //Failed
            _tprintf(L"FAILED thread %d\n", dwThreadID);
        }
    }

    return 0;
}

and I got no fails. So how would you explain it?

Community
  • 1
  • 1
c00000fd
  • 20,994
  • 29
  • 177
  • 400
  • Some processors, x86 being a leading example, guarantee a *lot* more for multithreaded programs than other processors do, or than the C or C++ standards require. – Jerry Coffin Jan 23 '14 at 21:42
  • I would explain it by saying that sometimes the result of undefined behavior is to do what you expect. – Casey Jan 23 '14 at 23:32

1 Answers1

2

In Intel, reads and writes to aligned words are atomic operations (atomic in the sense that other processors will see either the original or the new value).

Note that this does not mean that you should not provide synchronization mechanism. This test case is one in which threads just write and read new values into the same variable. If they were providing some sort of operation that involved a read/write for the update it could fail (say 10 threads incrementing the variable by 100, the variable at the end might not have been incremented by 1000 total!) and that there are no other variables in play (where compiler/cpu reordering could cause other issues).

David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
  • Well, yes obviously, if the same thread then tries to read from my `gdwSharedVal01` variable after updating it, it won't have the same value. My main concern was the integrity of the primitive type itself (per single operation.) – c00000fd Jan 23 '14 at 22:39
  • So, say, if I know for sure that my code will run on an x86/x64 Windows-only machine, Windows XP and up, can I safely skip synchronization for an example as the one I gave above? – c00000fd Jan 23 '14 at 22:40
  • 1
    @c00000fd: That particular example will work. Add a minimal amount of complexity around it, and it most probably will fail. Is an `int` initialized to 0 and set by one thread to 1 a valid synchronization mechanism? Only if the only shared state is that `int`. – David Rodríguez - dribeas Jan 23 '14 at 22:51