5

Anyone know whether primitive global variable is thread safe or not?

// global variable
int count = 0;

void thread1()
{
   count++;
}

void thread2()
{
   count--;
   if (count == 0) print("Stuff thing");
}

Can I do it this way without any lock protection for count?

Thank you.

Björn Pollex
  • 75,346
  • 28
  • 201
  • 283
longbkit
  • 1,218
  • 1
  • 13
  • 20

6 Answers6

8

This is not threadsafe. You have a race-condition here. The reason for that is, that count++ is not necessarily atomic (means not a single processor operation). The value is first loaded, then incremented, and then written back. Between each of these steps, the other thread can also modify the value.

Björn Pollex
  • 75,346
  • 28
  • 201
  • 283
3

No, it's not. It may be, depending on the implementation, the compile-time options and even the phase of the moon.

But the standard doesn't mandate something is thread-safe, specifically because there's nothing about threading in the current standard.

See also here for a more detailed analysis of this sort of issue.

If you're using an environment where threads are supported, you can use a mutex: examine the pthread_mutex_* calls under POSIX threads, for example.

If you're coding for C++0x/C++11, use either a mutex or one of the atomic operations detailed in that standard.

Community
  • 1
  • 1
paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
1

It will be thread safe only if you have 1 CPU with ++ and -- atomic operations on your PC.

If you want to make it thread safe this is the way for Windows:


LONG volatile count = 0;

void Thread1()
{
    ::InterlockedIncrement( &count );
}

void Thread2()
{
    if(::InterlockedDecrement( &count ) == 0 )
    {
       printf("Stuf");
    }
}


Sasha
  • 846
  • 5
  • 9
  • 2
    _Only if the memory read/modify/write cycle itself is atomic_ will that be the case. Even a 1-CPU machine can multi-task and get switched out mid-cycle. – paxdiablo Jun 23 '11 at 07:47
  • You are right. I probably had to mention that only for 1 CPU with ++ -- atomic operations. – Sasha Jun 23 '11 at 07:51
  • then this would be a good time to update your answer, nudge nudge wink wink :-) – paxdiablo Jun 23 '11 at 07:53
1

You need two things to safely use an object concurrently by two threads or more: atomicity of operations and ordering guarantees.

Some people will pretend that on some platforms what you're attempting here is safe because e.g. operations on whatever type int stands for those platforms are atomic (even incrementing or whatever). The problem with this is that you don't necessarily have ordering guarantees. So while you want and know that this particular variable is going to be accessed concurrently, the compiler doesn't. (And the compiler is right to assume that this variable is going to be used by only one thread at a time: you don't want every variable to be treated as being potentially shared. The performance consequences would be terrible.)

So don't use primitive types this way. You have no guarantees from the language and even if some platforms have their own guarantees (e.g. atomicity) you have no way of telling the compiler that the variable is shared with C++. Either use compiler extensions for atomic types, the C++0x atomic types, or library solutions (e.g. mutexes). And don't let the name mislead you: to be truly useful, an atomic type has to provide ordering guarantees along with the atomicity that comes with the name.

Luc Danton
  • 34,649
  • 6
  • 70
  • 114
0

It is a Global variable and hence multiple threads can race to change it. It is not thread safe.

Use a mutex lock.

Alok Save
  • 202,538
  • 53
  • 430
  • 533
  • In this particular scenario (incrementing) there are even faster options (OS specific), such as InterlockedIncrement – Assaf Lavie Jun 23 '11 at 07:37
  • @gigantt.com: The Q doesn't mention any platform so i assumed the solution expected should be reasonably platform independent. – Alok Save Jun 23 '11 at 07:40
  • Sure. Just thought I'd mention it. Also worth mentioning the GCC atomic builtins: http://gcc.gnu.org/onlinedocs/gcc-4.4.3/gcc/Atomic-Builtins.html#Atomic-Builtins – Assaf Lavie Jun 23 '11 at 07:44
  • @gigantt.com: Sure, worthwhile to mention, perhaps you can put that in an answer, as the information does add value. – Alok Save Jun 23 '11 at 07:49
0

In general, no, you can't get away with that. In your trivial example case, it might happen to work but it's not reliable.

Pete Wilson
  • 8,610
  • 6
  • 39
  • 51
  • 1
    Careful, Pete, it's quite possible for a memory write to update half a value then get switched out before updating the second half (think 8051 CPU for example with its byte-only operations). So you may end up in the transition of a 16-bit value from 255 to 256, with 0 or 511 depending on which half got updated first. In other words, its only safe if you know how the underlying stuff operates. It's _never_ safe for portable code. – paxdiablo Jun 23 '11 at 07:44