Being Threadsafe - An Introduction to the Pitfalls of Parallelism
Written by Mike James   
Friday, 31 May 2024
Article Index
Being Threadsafe - An Introduction to the Pitfalls of Parallelism
Making a thread safe
Concurrent exectution and exclusion
Locking in practice
Deadlock

For example, the previous code can be written in a more robust way as:

lock(this)
{
int temp=count+1;
Thread.Sleep(2);
if (temp != count + 1)
{
return;
}
count = temp;
};

Notice that while this is rather more foolproof than using the basic Monitor methods a thread that doesn’t play by the rules and simply accesses the resource will spoil everything.

The point is that you can’t enforce locking, just hope that everyone remembers to use it.

There are other Monitor methods that are sometimes useful.

For example the TryEnter method will attempt to acquire a lock, after waiting for a specified time, but will allow the thread to continue if the lock cannot be acquired.

Clearly in this case you need to test the return value (a Boolean) to see if the lock has been acquired and do something different if it hasn’t. The Wait method will allow the thread that currently has the lock to free it and allow other threads to acquire it while it waits for it to be signalled by another thread attempting to acquire the lock again. Another thread, one that currently has the lock, can signal to the next waiting thread (or to all waiting threads) to try to acquire the lock by using the pulse or pulseall method.

To understand how this might be used consider a thread that processes a buffer that is filled by another thread. The processing thread can call wait when it has finished processing the buffer and allow the filling thread to access it. As soon as the filling thread has finished its work it can use pulse to tell the processing thread to try to acquire the lock and start work again.

The clever part is that this mechanism generalises to multiple work-creating and work-consuming threads and they can all queue in an orderly fashion to access the resource using wait and pulse.

Deadlock

There are other problems with locking and the most celebrated is perhaps the deadlock condition.

Put simply, if thread A locks resource one and thread B locks resource two everything is fine unless thread A also wants a lock on resource two before it can complete and if thread B needs a lock on resource one before it can complete.

The result is that both threads spend forever waiting for the other to finish and release the resource.

This is deadlock and it can occur in much more complicated ways than this simple “A waits for B which waits for A” situation.

It is possible to create a deadlock ring of dependency by having A wait for B, which waits for  C, which waits for D which is waiting for A.

There isn’t much you can do about deadlock except to be aware of it and design your access strategies with a great deal of care. You can try to avoid locking threads on more than one lock at a time but this can slow things down to unacceptable levels as threads have to wait while another thread acquires an oversized lock on resources, some of which it isn’t actually using.

A better strategy is to attempt to acquire all of the locks that a thread needs to complete at the start and release any that have been acquired if it isn’t possible to acquire them all. Again this can result in a loss of performance.

The bottom line is that multi-threading with locks isn’t easy and carries the seeds of disaster. Multithreading without locks is easy but is always guaranteed to be a disaster.

Beyond the Monitor

You can do most of what you need to with nothing but the Monitor but .NET does provide other locking facilities.

For example the Mutex provides locking across process boundaries and the Semaphore can be used to control the number of threads that can access a resource. All of these work in similar ways to the Monitor and you should have no problems in understanding how they work – but if the Monitor does the job then use it.

There are also new facilities for creating parallel programs within .NET but these to have their problems. Lookout for a future article on the topic.

  • Mike James is the author of Deep C#: Dive Into Modern C#,  which provides a “deep dive” into various topics that are important or central to the language.

 

To be informed about new articles on I Programmer, sign up for our weekly newsletter, subscribe to the RSS feed and follow us on Twitter, Facebook or Linkedin.

 

Banner


Raspberry Pi CM5 - Expensive And Undocumented
27/11/2024

So the unexpected has happened - the Compute Module 5 has been launched. But it simply emphasises some problems with adopting the Pi as an IoT device.



DuckDB And Hydra Partner To Get DuckDB Into PostgreSQL
11/11/2024

The offspring of that partnership is pg_duckdb, an extension that embeds the DuckDB engine into the PostgreSQL database, allowing it to handle analytical workloads.


More News

espbook

 

Comments




or email your comment to: comments@i-programmer.info

<ASIN:B09FTLPTP9>

 



Last Updated ( Friday, 31 May 2024 )