Как заблокировать переменный адрес очереди вместо критического раздела? - PullRequest
1 голос
/ 03 марта 2010

У меня есть 2 потока и глобальная очередь, один поток (t1) отправляет данные, а другой (t2) извлекает данные, я хотел синхронизировать эту операцию без использования функции, где мы можем использовать эту очередь с критическим разделом с помощью Windows API .

Очередь глобальная, и я хотел знать, как синхронизировать, это делается путем блокировки адреса очереди?.

Ответы будут высоко оценены. :)

Можно ли использовать Boost Library для решения вышеуказанной проблемы?

Спасибо

Ответы [ 3 ]

3 голосов
/ 03 марта 2010

Один подход состоит в том, чтобы иметь две очереди вместо одной:

  • Поток производителя помещает элементы в очередь A.
  • Когда поток потребителя хочет вытолкнуть элементы, очередь A заменяется пустой очередью B.
  • Поток производителя продолжает отправлять элементы в новую очередь A.
  • Потребитель непрерывно потребляет элементы из очереди B и очищает их.
  • Очередь A заменяется на очередь B и т. Д.

Единственная блокировка / блокировка / синхронизация происходит, когда очереди меняются местами, что должно быть быстрой операцией, поскольку на самом деле нужно поменять местами два указателя.

0 голосов
/ 03 марта 2010

Другим способом решения этой проблемы является динамическое выделение очереди и назначение ее указателю. Значение указателя передается между потоками, когда элементы должны быть выведены из очереди, и вы защищаете эту операцию критическим разделом. Это означает блокировку для каждого добавления в очередь, но гораздо меньше конфликтов при удалении элементов.

Это хорошо работает, когда у вас есть много предметов между постановкой в ​​очередь и снятие очереди, и менее эффективно с несколькими предметами.

Пример (я использую некоторый заданный класс блокировки RAII для выполнения блокировки). Также обратите внимание ... действительно безопасно только при удалении одного потока.

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;
}
0 голосов
/ 03 марта 2010

Я думал, что вы могли бы создать очередь с этими условиями, не используя никакой атомарности или каких-либо потоковобезопасных вещей вообще?

, как если бы это был просто буфер круга, один поток контролировал указатель чтения, а другой контролировалуказатель записи.оба не обновляются, пока не закончат чтение или запись.и это просто работает?

единственная трудность заключается в определении при чтении == записи, заполнена ли очередь или пуста, но вы можете преодолеть это, просто поместив один фиктивный элемент в очередь

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