6

If I have two threads and one global variable (one thread constantly loops to read the variable; the other constantly loops to write to it) would anything happen that shouldn't? (ex: exceptions, errors). If it, does what is a way to prevent this. I was reading about mutex locks and that they allow exclusive access to a variable to one thread. Does this mean that only that thread can read and write to it and no other?

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
randy newfield
  • 1,221
  • 3
  • 25
  • 38

5 Answers5

5

Would anything happen that shouldn't?

It depends in part on the type of the variables. If the variable is, say, a string (long array of characters), then if the writer and the reader access it at the same time, it is completely undefined what the reader will see.

This is why mutexes and other coordinating mechanisms are provided by pthreads.

Does this mean that only that thread can read and write to it and no other?

Mutexes ensure that at most one thread that is using the mutex can have permission to proceed. All other threads using the same mutex will be held up until the first thread releases the mutex. Therefore, if the code is written properly, at any time, only one thread will be able to access the variable. If the code is not written properly, then:

  • one thread might access the variable without checking that it has permission to do so
  • one thread might acquire the mutex and never release it
  • one thread might destroy the mutex without notifying the other

None of these is desirable behaviour, but the mere existence of a mutex does not prevent any of these happening.

Nevertheless, your code could reasonably use a mutex carefully and then the access to the global variable would be properly controlled. While it has permission via the mutex, either thread could modify the variable, or just read the variable. Either will be safe from interference by the other thread.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • the global data is an integer for a sleep timer. one thread determains the sleep from other data it gets from other places, while the other thread does a function and then sleeps for the time the first thread set to the variable. the sleep global variable does not need to be "current" as in if thread 1 sets it to 12 then thread 2 reads it as 12 just as thread 1 changes it to 16. would it still be advisable to implement locks or could i just leave it as is seeing as the sleep time does not need to be the current time thread 1 sets. – randy newfield Mar 14 '12 at 07:13
  • The chances are reasonable that an `int` will be 'atomic' for read and write, but that's not guaranteed. You most probably would get away without a mutex protecting this value, but you could run into funny problems, and timing problems are hell to reproduce and diagnose. I'd probably not risk it, but it depends on how critical your system/application is. If the reader is going to sleep for 12 seconds at a time, it won't matter much if the writer frenetically changes its mind about the time. So, **yes** you are at liberty to take a (calculated) risk. But don't come crying if things go wrong. – Jonathan Leffler Mar 14 '12 at 07:17
  • the sleep time is not actually in seconds that was just an example. the writing thread updates the sleep time once per second while the reading thread may read the time many times before it is updated again, because the sleep time is actually large integer of iterations for a empty loop to replace a system call to usleep(). anyways i think i will implement mutex locking anyways. it wont impede the speed of the threads to much and it will most likely solve future headaches. – randy newfield Mar 14 '12 at 07:33
  • A busy loop is always a design flaw. Relying on the speed of a (modern, variable speed) CPU is also a design flaw. What problem were these design flaws meant to solve? – Brendan Mar 14 '12 at 08:22
2

Does this mean that only that thread can read and write to it and no other?

It means that only one thread can read or write to the global variable at a time.
The two threads will not race amongst themselves to access the global variable neither will they access it at the same time at any given point of time.

In short the access to the global variable is Synchronized.

Community
  • 1
  • 1
Alok Save
  • 202,538
  • 53
  • 430
  • 533
  • would it be safe to just let them read and write without locking or should i make it so that each thread locks the variable and does its reading or writing then unlocking it when its done? – randy newfield Mar 14 '12 at 06:27
  • Not sure I agree with this answer -- only one process can take the mutex lock, but an programming error in the other process can easily allow it to read/write the variable without having taken the lock first. – Soren Mar 14 '12 at 06:29
  • @bobmoch: In general, it is not safe to read and write without locking. In some very specific cases, it is safe, but you are better off not worrying about it since uncontested locks are very fast. – Dietrich Epp Mar 14 '12 at 06:30
  • @Soren: There does not exist a programming language which is "safe" from programming errors. – Dietrich Epp Mar 14 '12 at 06:31
  • @bobmoch: You **must** lock the global variable while read or write operation else You would have Race conditions. – Alok Save Mar 14 '12 at 06:34
  • how would i lock a single variable from two different threads? im reading http://www.yolinux.com/TUTORIALS/LinuxTutorialPosixThreads.html in the mutexes section and it looks like if i use the same mutex handle in both threads it will check to see if its locked and if it is it will block until it becomes free. is this right or would i need a different mutex variable for each thread? – randy newfield Mar 14 '12 at 06:36
  • @bobmoch: You use one mutex. All threads must share the mutex. You cannot copy a mutex. (You can use multiple mutexes if you have different things you want to lock, but then you have to be more careful about possible deadlocks.) – Dietrich Epp Mar 14 '12 at 06:56
1

First; In C/C++ unsynchronized read/write of variable does not generate any exceptions or system error, BUT it can generate application level errors -- mostly because you are unlikely to fully understand how the memory is accessed, and whether it is atomic unless you look at the generated assembler. A multi core CPU may likely create hard-to-debug race conditions when you access shared memory without synchronization.

Hence

Second; You should always use synchronization -- such as mutex locks -- when dealing with shared memory. A mutex lock is cheap; so it will not really impact performance if done right. Rule of thumb; keep the lcok for as short as possible, such as just for the duration of reading/incrementing/writing the shared memory.

However, from your description, it sounds like that one of your threads is doing nothing BUT waiting for the shared meory to change state before doing something -- that is a bad multi-threaded design which cost unnecessary CPU burn, so

Third; Look at using semaphores (sem_create/wait/post) for synchronization between your threads if you are trying to send a "message" from one thread to the other

Soren
  • 14,402
  • 4
  • 41
  • 67
1

As others already said, when communicating between threads through "normal" objects you have to take care of race conditions. Besides mutexes and other lock structures that are relatively heavy weight, the new C standard (C11) provides atomic types and operations that are guaranteed to be race-free. Most modern processors provide instructions for such types and many modern compilers (in particular gcc on linux) already provide their proper interfaces for such operations.

Jens Gustedt
  • 76,821
  • 6
  • 102
  • 177
-1

If the threads truly are only one producer and only one consumer, then (barring compiler bugs) then

1) marking the variable as volatile, and

2) making sure that it is correctly aligned, so as to avoid interleaved fetches and stores

will allow you to do this without locking.

tbert
  • 2,089
  • 13
  • 14
  • This only works on (1) some architectures with (2) some data types and (3) if you don't need blocking semantics and (4) if the global variable does not refer to shared state. This is impossible to recommend except in very unusual circumstances. (By the way, fetches and stores **will** be reordered -- by both the CPU and by the compiler -- whether or not the data is aligned. Using `volatile` only prevents certain specific changes by the compiler, and the CPU is still free to reorder it.) – Dietrich Epp Mar 14 '12 at 07:01