views:

172

answers:

3

Hi,

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

A: 

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;
      }
};
matt
It's possible to write a lock-free queue -- but it's not trivial. See: http://www.drdobbs.com/cpp/210604448 for one example.
Jerry Coffin
@Jerryyeah I've co-written a lock free queue but they still kinda 'lock' and use atomics. the above should work and be completely lock free and thread safe if used in the situation he has specified shouldn't it? there is no reason to the trouble of making a proper lock free queue that would be more generally thread safe if the above would work for what he wants? I would be interested to know if the above code == fail.
matt
There might be some conditions under which it'll work, but you'd need some explicit constraints (beyond what he's specified). For example, it seems to require that read and writing an int be implicitly atomic. If Pop() reads from writepoint at the same time as pop is writing to it, and reads/writes aren't atomic, it can/will have a serious problem.
Jerry Coffin
+2  A: 

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.

Ates Goral
A: 

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;
}
Snazzer