-1

Consider my Delphi program has two threads.

The main one of course and a runner thread (always running it never stops).

I would like to know what is the best way to update variables (16 atomic integers) in the runner thread without creating problems.

The runner thread consist of always sending low level data over USB (time critical <10 ms).

I only need one way since the runner thread never report to the main thread.

Any ideas?

Thank you!

ELCouz
  • 572
  • 2
  • 6
  • 17
  • http://web.archive.org/web/20060305174604/http://www.pergolesi.demon.co.uk/prog/threads/ToC.html – Free Consulting Jan 02 '15 at 03:11
  • 1
    http://stackoverflow.com/questions/1806339/is-it-better-to-use-tthreads-synchronize-or-use-window-messages-for-ipc-betwe http://stackoverflow.com/questions/5600532/synchronizing-sending-data-between-threads http://stackoverflow.com/questions/438945/cross-thread-communication-in-delphi – bummi Jan 02 '15 at 09:34
  • omnithreadlibrary looks very nice to use library. Luckily I'm using right now XE2 but the future of it look uncertain (It's the last version supported). Maybe another dying delphi library that i've spent a lot of time to integrate in my code. – ELCouz Jan 02 '15 at 12:53
  • 1
    OmniThreadLibrary is far from dead – Leonardo Herrera Jan 02 '15 at 14:15

2 Answers2

3

If you are considering in exposing some variables of the running thread and then changing these with your main thread don't! I realy mean it. DON'T!

WHy not? Becouse your main thread could never know when it is safe to change these variables and could end up changing them at the same time when they are being read by runner thread.

So instead I would go and create a record containing all the variables but that record is outside of your runners scope.

Then I would make sure that all changes that are made to the data in this record are being protected by critical section. This is to ensure only one thread is accesing that record at the same time.

Then in your runner thread I would be reading the data from that record an making a local copy (this one is within your running thread). Now reading of the record is again being protected by critical section.

Now you might be asking yourself why would I read the data for these variables from the running thread. The main reason for this is that your running thread is the one who now when the data in those variables can be updated and when not.

And the reason why I would store variables data in a record is to make enterin and leaving of critical section as fast as psoible.
You see when you are using critical sections entering the critical section will lock all the variables that you will be accessing inside the critical section. So if you have multiple of these that proces may take a while becouse the variables are being locked one by one. But if you have all your variables stored inside a record entering the critical section will lock the whole record at once.
Same thing applies when leaving critical section and acces to variables is being unlocked.

SilverWarior
  • 7,372
  • 2
  • 16
  • 22
  • 3
    As long as you're protecting all the variables with a single critical section, it doesn't matter whether they all live together in a single record. The variables don't get locked in any case. – Rob Kennedy Jan 02 '15 at 04:29
  • Locking a critical section locks the critical section - **not** individual variables. It does not matter how many values are behind the critical section. The critical section has **abosultely no knowledge** of the variables you will be accessing. – Disillusioned Jan 02 '15 at 09:00
  • To reiterate: your last paragraph is entirely wrong. The only way to get a reduced lock time in the scenario you're describing is to: **(1)** Lock **(2)** Replace a pointer to a record (or replace an object reference). **(3)** Unlock ..... If that's what you're trying to explain, you need to reword that section. – Disillusioned Jan 02 '15 at 10:15
  • Even with atomic vars? Quote from stackoverflow: If a thread A executes atomic write and a thread B atomic read of the same data at the same time, the data read by thread B is always consistent - it is impossible that some bits read by thread B are obtained from the current write operation and some bits from the previous write (by thread A) – ELCouz Jan 02 '15 at 12:44
  • The idea is NOT to "lock the variables". The idea is that *all* threads lock the critical section which represents any number of variables. As long as you have one lock, you can access as many variables as needed. But, they're all locked to one thread at a time (so even if you have 2 variables, while thread A is accessing one of them, thread B cannot access even the other variable). As everyone else is saying, the critical sections have absolutely no idea what it is responsible for locking. – Jerry Dodge Jan 02 '15 at 15:24
  • Variables are not atomic. It is operations that can be atomic. Or not. With respect to each other. A variable can be incremented atomically. Or non-atomically. Another issue is that of tearing, or partial read/write. On many platforms, aligned variables are never subject to tearing. – David Heffernan Jan 04 '15 at 10:11
3

Your question is very broad.

There are a number of options available, and the best for you depends on your specific needs and environment. You need to provide more details about the kind of data you want your main thread to update on the runner thread. How often are the updates expected? You've stated that the runner thread is time-critical - which means that you want to keeping locking (PS: The main source of slowdowns in multi threaded applications is when different threads compete for the same locks. Referred to as lock contention.) to a barest minimum. (As I said: It depends on your needs.)

PS: Must all data set by the main thread be used? Or is it acceptable for the runner thread to simply use the latest available data ignoring however many intermediate values may have been assigned? The answer to this question yields fundamentally different options.


For example atomic updates can be performed without any locking. Look at TThread.Terminated. It's a simple value. You do not need any locks to update this particular value. There are no problematic race conditions because the processor will read or write the value as an entire atomic unit.

Even if you're updating at the same time your while not Terminated loop is reading the value - there won't be a problem. The update will either happen just before the read (resulting in the thread exiting the loop) or it will happen just after (resulting in one more iteration before the loop exits at the next read).

PS: It's important to be aware that setting string values is not an atomic operation.

Now you've indicated the main thread doesn't need to read the runner thread's data at all. But I'll use the possibility as a contrasting example. If you needed to increment an integer value on the runner thread, that would need protection. This is because incrementing a value is a multi-step operation:

  • Read the value.
  • Perform a calculation on the value that was read.
  • Write a new value.

If the runner thread an main thread are using the value at the same time, you may get inconsistent results.

Another situation that can cause problems is where data is made up of a number of values that interact with each other. E.g. NoOfUnits and MassPerUnit are used in combination to determine TotalMass. Updating these values independently can result in race conditions causing inconsistent behaviour.

Silver Warrior's answer provides a technique to protect multiple values. Though be aware that there are some serious errors in the current version of that answer.

Note if you keep your data encapsulated in a separate object, it is posisble to update your runner thread's data without any locks because you can update a pointer value atomically. (VERY NB: There are a number of special rules you'd have to follow, and you'd need to figure out how to avoid memory leaks... But that's detail for a more specific question.)

Another option is to implement your runner thread as a message queue. I.e. when the main thread wants to change values, it sends a message to the runner thread. The runner will only process the changed values instruction when it's "safe" to do so. (Again the feasability of this depends on your specific requirements.)


And as a final note there are some additional concerns over and above protecting data from race conditions. Exactly how time critical is your runner thread? How much processing does it do? Does it need to respond quickly to certain events? If so, what events?

Answers to these questions are important in understanding the ideal structure of your runner thread's main loop. For example, a "busy loop" (a loop that might iterate without doing anything just to ensure it never pauses) would make the thread highly responsive, but starve the machine of resources and slow it down as a whole. By comparison, message queues would typically run a loop processing messages until there are none left, then put the thread into a "wait-state" until the next message is received.

PS: Another potential source of contention and slow-downs is the memory manager. If both your main and runner threads perform a significant number of heap allocations/deallocations, you may get lock contention in areas you didn't even explicitly code.

Community
  • 1
  • 1
Disillusioned
  • 14,635
  • 3
  • 43
  • 77
  • The runner thread have a time-window of <10 ms to send the data. Not a lot of processing in reality done in runner thread, few simple loops nothing too complex. I need to take more time to studies the possibilities offered on this nice topic. For now, I've found that writing atomic vars is thread safe as long that ONLY the main thread (one thread) is writing to them. – ELCouz Jan 02 '15 at 12:39