ContentsIntroductionBackground Text and Code Disclaimer Part I Implementation overview Circular FIFO - How it works Implementation Improvements and code notes Part II Making It Work Atomic Reordering Compiler Reordering CPU Reordering Reorder Summary Making it Work: Conclusion What's Wrong with this Code? Finally ... References History This text was also published at http://www.codeproject.com/KB/threads/LockFree.aspx. IntroductionI attended Herb Sutter's Effective Concurrency Seminar (Europe 2009), a very inspiring seminar where he approached multi-core targeted programming. One thing we worked with was the dangers of Lock-Free programming and hazards of volatile in C++. I got curious since I've seen a couple of simple single producer -> single consumer Circular Queue implementations that are implemented as Lock-Free and using just that, the important state variables defined as volatile. So how come they work (being used for years), or do they? Volatile as you might know is not intended for multi-threading but for accessing hardware devices (and more). It turns out that volatile plays a minor role and that it's mostly the compiler and computer platform that decides whether or not this kind of Circular Queues will be safe to use. I have implemented a thread safe circular queue in C++ and explain in this article when it works and when it will not work. This is an area outside the scope of the C++ standard (until C++0x) and is suitable for (at least) x86 or x64 platforms. For those of you who want to read up on the inner workings of the
Circular Queue, I have provided a short description of it and my
implementation. For those of you who are already familiar with it and
just want to know if this thread safe circular queue is for real, just
go straight to the Implementation section. BackgroundIf an application does multiple tasks simultaneously, then multi-threading would be useful. In this particular example, I show a circular FIFO queue that can be used for communicating between maximum two threads. This scenario is often referred to as the single producer -> single consumer problem. Example: High priority task (GUI, data recorder, etc.) that must not be delayed in lengthy operations and require them to finish in the same order as they are started. The solution: Delegate the time consuming jobs to a worker thread. Using a FIFO queue makes it possible for the delegation and processing of tasks to be done asynchronously i.e. the producer and consumer each work in their own thread and therefore reads (remove) can be concurrent with writes (add). The FIFO qualities guarantee that delegated tasks are processed in the same order as they were added. The producer can add new tasks to the queue as long as the queue is not full, and the consumer can remove tasks from the queue as long as the queue is not empty. It's worth mentioning that Circular Queues are commonly used when:
But all of these examples are outside the scope of this article. Text and Code DisclaimerI decided to keep this example simple and not go into all the nitty-gritty details that an optimal solution might have. This is why any writes to a full queue or any reads from an empty queue are just plainly ignored. For the same reason, the queue is not optimized to avoid false sharing. It can be argued that under some conditions like in my test of queue the queue will suffer from false sharingif both threads are on different cores. However under other conditions than in my silly test, this is not necessarily the case. What decides if there will be false sharing depends on what is stored within the array: integers, pointers or whole objects and if they are on the same cache line as both tail and head indexes are accessing. IF there is false sharing, it's not always a very bad thing. It all depends on the frequency of the false sharing, i.e., how often the consumer/producer threads are trying to access the same cache line. Part I
| updated 2009-11-15
Code Download
|
Many thanks to http://www.csharpindepth.com/CodeFormatterTool.aspx which I used to convert c++ code to html




