Question

I have 2 threads and global Queue, one thread (t1) push the data and another one(t2) pops the data, i wanted to sync this operation without using function where we can use that queue with critical section using windows API.

The Queue is global, and i wanted to know how to sync, is it done by locking address of Queue?.

Answers will be much appreciated. :)

Is it possible to use Boost Library for the above problem ?

Thank you

Was it helpful?

Solution

One approach is to have two queues instead of one:

  • The producer thread pushes items to queue A.
  • When the consumer thread wants to pop items, queue A is swapped with empty queue B.
  • The producer thread continues pushing items to the fresh queue A.
  • The consumer, uninterrupted, consumes items off queue B and empties it.
  • Queue A is swapped with queue B etc.

The only locking/blocking/synchronization happens when the queues are being swapped, which should be a fast operation since it's really a matter of swapping two pointers.

OTHER TIPS

I thought you could make a queue with those conditions without using any atomics or any thread safe stuff at all?

like if its just a circle buffer, one thread controls the read pointer, and the other controls the write pointer. both don't update until they are finished reading or writing. and it just works?

the only point of difficulty comes with determining when read==write whether the queue is full or empty, but you can overcome this by just having one dummy item always in the queue

class Queue
{
     volatile Object* buffer;
     int size;
     volatile int readpoint;
     volatile int writepoint;

     void Init(int s)
     {
          size = s;
          buffer = new Object[s]; 
          readpoint = 0;
          writepoint = 1;
     }

     //thread A will call this
     bool Push(Object p)
     {
         if(writepoint == readpoint)
         return false;
         int wp = writepoint - 1;
         if(wp<0)
             wp+=size;
         buffer[wp] = p;
         int newWritepoint = writepoint + 1;
         if(newWritepoint==size)
            newWritePoint = 0;
         writepoint = newWritepoint;
         return true;
      }

      // thread B will call this
      bool Pop(Object* p)
      {
          writepointTest = writepoint;
          if(writepointTest<readpoint)
               writepointTest+=size;
          if(readpoint+1 == writepoint)
              return false;
          *p = buffer[readpoint];

         int newReadpoint = readpoint + 1;
         if(newReadpoint==size)
            newReadPoint = 0;
         readpoint = newReadPoint;
         return true;
      }
};

Another way to handle this issue is to allocate your queue dynamically and assign it to a pointer. The pointer value is passed off between threads when items have to be dequeued, and you protect this operation with a critical section. This means locking for every push into the queue, but much less contention on the removal of items.

This works well when you have many items between enqueueing and dequeueing, and works less well with few items.

Example (I'm using some given RAII locking class to do the locking). Also note...really only safe when only one thread dequeueing.

queue* my_queue = 0;
queue* pDequeue = 0;
critical_section section;

void enqueue(stuff& item)
{
   locker lock(section);
   if (!my_queue)
   {
      my_queue = new queue;
   }
   my_queue->add(item);
}

item* dequeue()
{
   if (!pDequeue)
   {  //handoff for dequeue work
      locker lock(section);
      pDequeue = my_queue;
      my_queue = 0;
   }
   if (pDequeue)
   {
      item* pItem = pDequeue->pop(); //remove item and return it.
      if (!pItem)
      {
         delete pDequeue;
         pDequeue = 0;
      }
      return pItem;
   }
   return 0;
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top