0

I have an iterative process coded in C++ which takes a long time and am considering converting my code to use multiple threads. But I am concerned that it could be very complicated and risk lock-ups and bugs. However I suspect that for this particular problem it may be trivial, but I would like confirmation.

I am hoping I can use threading code which is a s simple as this here.

My program employs large amounts of global arrays and structures. I assume that the individual threads need not concern themselves if other threads are attempting to read the same data at the same time.

I would also assume that if one thread wanted to increment a global float variable by say 1.5 and another thread wanted to decrement it by 0.1 then so long as I didn't care about the order of events then both threads would succeed in their task without any special code (like mutexs and locks etc) and the float would eventually end up larger by 1.4. If all my assumptions are correct then my task will be easy - Please advise.

EDIT: just to make it absolutely clear - it doesn't matter at all the order in which the float is incremented / decremented. So long as its value ends up larger by 1.4 then I am happy. The value of the float is not read until after all the threads have completed their task.

EDIT: As a more concrete example, imaging we had the task of finding the total donations made to a charity from different states in the US. We could have a global like this:

float total_donations= 0;

Then we could have 50 separate threads, each of which calculated a local float called donations_from_this_state. And each thread would separately perform:

total_donations += donations_from_this_state;

Obviously which order the threads performed their task in would make no difference to the end result.

Community
  • 1
  • 1
Mick
  • 8,284
  • 22
  • 81
  • 173
  • 1
    Your example about incrementing and decrementing is *not* going to work without some extra handling. You'd need to use mutexes, atomic variables, etc when dealing with global variables. Increment and decrement operations are not atomic unless you use atomic types. – Cory Kramer Aug 02 '16 at 13:18
  • First step I would do is refactor all the globals into function arguments and see what is part of the shared state and what is not. The fact that a variable can be incremented and decremented tells me that the order of these operations may well influence other parts of the algorithm... – rubenvb Aug 02 '16 at 13:19
  • Writing multi-threaded code is **never** trivial. – Pete Becker Aug 02 '16 at 14:32

1 Answers1

5

I assume that the individual threads need not concern themselves if other threads are attempting to read the same data at the same time.

Correct. As long as all threads are readers no synchronization is needed as no values are changed in the shared data.

I would also assume that if one thread wanted to increment a global float variable by say 1.5 and another thread wanted to decrement it by 0.1 then so long as I didn't care about the order of events then both threads would succeed in their task without any special code (like mutexs and locks etc) and the float would eventually end up larger by 1.4

This assumption is not correct. If you have two or more threads writing to the same shared variable and that variable is not internally synchronized then you need external synchronization otherwise your code has undefined behavior per [intro.multithread]/21

The execution of a program contains a data race if it contains two conflicting actions in different threads, at least one of which is not atomic, and neither happens before the other. Any such data race results in undefined behavior.

Where conflicting action is specified by [intro.multithread]/4

Two expression evaluations conflict if one of them modifies a memory location (1.7) and the other one accesses or modifies the same memory location.

NathanOliver
  • 171,901
  • 28
  • 288
  • 402
  • ....ah, I think it's dawning on me - you're saying that adding a float to a global is not atomic - its is somehow a multi-step operation. – Mick Aug 02 '16 at 13:39
  • @Mick Yes it is not atomic(at least not gauranteed). If you need a atomic operation you should use `std::atomic`. More then likely in the scenario you described you will get what you want but there is no requirement. See this: http://stackoverflow.com/questions/37568740/operator-prefix-with-threads – NathanOliver Aug 02 '16 at 13:41
  • I converted the SO question to use float and it gives the correct answer every time. So whilst not guaranteed, it appears I could get away with it in practice. – Mick Aug 02 '16 at 14:16
  • @Mick I would still caution you to use `std::atomic`. It is a bad idea to have UB in your code. – NathanOliver Aug 02 '16 at 14:31