When you start programming with threading, you may start into some wonderful constructs known as non-blocking (or lock-free) containers. These data structures promise to give your threads free reign, running at unfettered speed, without stopping to wait on anything else in your program.

However, you can't do a lock-free list on typical CPUs (including x86); you can only do a fixed-size stack lock-free (and even so, it's not safe for arbitrary reading and writing at the same time); you CAN do a fixed-size queue lock-free (known as a non-blocking FIFO).

However, in the end, the threads will end up having to wait on SOMETHING. That something might be user input, or the advancement of time. Thus, every thread needs at least one blocking operation per loop. For the main thread, it'll be the message pump; for a worker thread, it'll be the queue of work items.

Note that function statics are NOT thread safe. If you're using them, you have to make sure to make initialization safe, or take them out.

  Object & accessor()
  {
    static Object instance; // not thread safe
    return instance;
  }

Note that if the first call to accessor() overlaps with the second call (on two different threads), one of them may return a reference to a not-yet-initialized object, or the object may be initialized twice, depending on compiler runtime implementation. The only work-around is to use a specific critical section:

  CRITICAL_SECTION staticInit;
  template< class T > class DeletePtr {
    public:
      // No constructor -- on purpose!
      ~DeletePtr() { delete ptr_; }
      T * ptr_;
  };

  Object & accessor()
  {
    static DeletePtr< Object > instance;
    if( !instance.ptr_ ) {
      EnterCriticalSection( &staticInit );
      if( !instance.ptr_ ) {
        instance.ptr_ = new Object;
      }
      LeaveCriticalSection( &staticInit );
    }
    return *instance.ptr_;
  }

This construct will make sure that:

  • 1) Initialization happens exactly once.
  • 2) You don't pay the price of a critical section on every access.
  • 3) The object actually gets deleted when the program exits.

It has the draw-back that two objects can't be initialized at the same time, but that's seldom an actual problem -- initialization is not on the inner-loop performance path.

When it comes to threading, there are a few concepts you really need to understand:

  • a) critical sections
  • b) producer/consumer patters
  • c) blocking, scheduling and pre-emption
  • d) stacks and contexts

If you go to a decent computer science program in college, they will teach you these things; any textbook on operating systems will also cover the area. I suggest you get one and read it if you're going to be doing significant programming with threads.