Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What are common concurrency pitfalls? [closed]

People also ask

What are concurrency issues?

Because many users access and change data in a relational database, the database manager must allow users to make these changes while ensuring that data integrity is preserved. Concurrency refers to the sharing of resources by multiple interactive users or application programs at the same time.

Have you ever written concurrent code what are it's most common pitfalls?

One of the biggest pitfalls is the use of concurrency in the first place. Concurrency adds a substantial design/debugging overhead, so you really have to examine the problem and see if it really calls for concurrency. Concurrency is certainly unavoidable in some domains, but when it is avoidable, avoid it.


There are lots of good answers and pointers on this thread already, but let me add one thing.

DO NOT RELY ON TESTING TO FIND RACE CONDITIONS AND DEADLOCKS

Assuming you have all the good development processes in place: unit tests for each component, smoke tests for each nightly build, requiring each developer's changes to pass tests before checkin , etc.

All that is well and good, but it leads to an attitude of "well, it passed the test suite, so it can't be a bug in my code." which will not serve you well in concurrent programming. Real-time concurrency bugs are fiendishly hard to reproduce. You can run a piece of code with a race condition a billion times before it fails.

You will have to adjust your process to put greater emphasis on code reviews, conducted by your best minds. Having a seperate code review just for concurrency issues only is not a bad idea.

You will have to put more emphasis on making your application self-debugging. That is, when you get a failure in the test lab or at your customer's site, you need to make sure enough information is captured and logged to let you do a definitive postmortem, since the odds of your being able to reproduce the bug report at your convenience are negligible.

You will have to put more emphasis on paranoid sanity checks in your code so that a fault is detected as close to the problem as possible, and not 50,000 lines of code away.

Be paranoid. very paranoid.


One is race condition, which basically is assuming that one piece of code will run before / after another concurrent piece of code.

There are also deadlocks, that is code A waits for code B to release resource Y, while code B is waiting for A to release resource X.


One of the biggest pitfalls is the use of concurrency in the first place. Concurrency adds a substantial design/debugging overhead, so you really have to examine the problem and see if it really calls for concurrency.

Concurrency is certainly unavoidable in some domains, but when it is avoidable, avoid it.


I teach concurrency a lot to my friends and co-workers. Here are some of the big pitfalls:

  • Assuming that a variable that is mostly read in many threads and only written in one thread doesn't need to be locked. (In Java, this situation may result in the reading threads never seeing the new value.)
  • Assuming that the threads will run in a particular order.
  • Assuming that the threads will run simultaneously.
  • Assuming that the threads will NOT run simultaneously.
  • Assuming that all of the threads will make forward progress before any one of the threads ends.

I also see:

  • Big confusions between thread_fork() and fork().
  • Confusions when memory is allocated in one thread and free()d in another thread.
  • Confusions resulting from the fact that some libraries are threadsafe and some are not.
  • People using spin-locks when they should use sleep & awake, or select, or whatever blocking mechanism your language supports.

Concurrency doesn't have many pitfalls.

Synchronizing access to shared data, however, is tricky.

Here are some questions anyone writing shared-data synchronization code should be able to answer:

  1. What is InterlockedIncrement?
  2. Why does InterlockedIncrement need to exist at an assembly language level?
  3. What is read write reordering?
  4. What is the volatile keyword (in c++) and when do you need to use it?
  5. What is a synchronization hierarchy?
  6. What is the ABA problem?
  7. What is cache coherency?
  8. What is a memory barrier?

"Shared everything" concurrency is an extremely leaky abstraction. Adopt shared nothing message passing instead.


One truth to keep in mind is that even if the initial developers get their tasking model working properly (which is a big if), then the maintenance team that follows will surely screw things up in unimaginable ways. The take-away from that is to limit the traces of concurrency throughout your system. Do your best to make sure that most of your system is blissfully unaware that concurrency is occurring. That gives fewer opportunities for people unfamiliar with the tasking model to inadvertently screw things up.

Too often people go thread/task crazy. Everything works on its own thread. The end result is that nearly every piece of code has to be intimately aware of threading issues. It forces otherwise simple code to be littered with locking and synchronization confuscations. Every time I’ve seen this, the system eventually becomes an unmaintainable mess. Yet, every time I’ve seen this, the original developers still insist it was a good design:(

Like multiple inheritance, if you want to create a new thread/task then assume you are wrong until proven otherwise. I can’t even count the number of times that I’ve seen the pattern Thread A calls Thread B thenThread B calls Thread C then Thread C calls D all waiting for a response from the previous thread. All the code is doing is making long-winded function calls through different threads. Don’t use threads when function calls work just fine.

Always remember, Message Queues are your best friend when you want to work concurrently.

I have found that creating a core infrastructure that handles nearly all the concurrency issues works best. If there are any threads outside of the core infrastructure that must talk to another piece of software then they must go through the core infrastructure. That way the rest of the system can remain concurrency unaware and the concurrency issues can be handled by the person(s) who hopefully understand concurrency.