Практически без блокировки производитель, потребитель - PullRequest
1 голос
/ 27 декабря 2011

У меня проблема с потребителем производителя, которую необходимо решить с небольшой модификацией - в параллельном потоке много параллельных производителей, но только один потребитель Когда у производителя нет места в буфере, он просто игнорирует элемент (не дожидаясь потребителя). У меня есть некоторый псевдокод C:

struct Element
{
   ULONG content;
   volatile LONG bNew;
}

ULONG max_count = 10;
Element buffer* = calloc(max_count, sizeof(Element));
volatile LONG producer_idx = 0;
LONG consumer_idx = 0;
EVENT NotEmpty;

BOOLEAN produce(ULONG content)
{
  LONG idx = InterlockedIncrement(&consumer_idx) % max_count;

  if(buffer[idx].bNew)
    return FALSE;
  buffer[idx].content = content;
  buffer[idx].bNew = TRUE;
  SetEvent(NotEmpty);
  return TRUE;
}

void consume_thread()
{
  while(TRUE)
  {
    Wait(NotEmpty);
    while(buffer[consumer_idx].bNew)
    {
      ULONG content = buffer[consumer_idx].content;
      InterlockedExchange(&buffer[consumer_idx].bNew, FALSE);
      //Simple mechanism for preventing producer_idx overflow
      LONG tmp = producer_idx;
      InterlockedCompareExchange(&producer_idx, tmp%maxcount, tmp);
      consumer_idx = (consumer_idx+1)%max_count;
      doSth(content);
    }
  }
}

Я не уверен на 100%, что этот код правильный. Вы видите какие-либо проблемы, которые могут возникнуть? Или, может быть, этот код может быть написан лучше?

Ответы [ 2 ]

0 голосов
/ 27 декабря 2011

Пожалуйста, прочитайте это: http://en.wikipedia.org/wiki/Memory_barrier

Стандарты C и C ++ не относятся к нескольким потокам (или нескольким процессорам), и поэтому полезность volatile зависит от компилятора и аппаратного обеспечения,Хотя volatile гарантирует, что энергозависимые чтения и энергозависимые записи будут происходить в точном порядке, указанном в исходном коде, компилятор может сгенерировать код (или ЦП может изменить порядок выполнения), так что энергозависимое чтение или запись будет переупорядочено относительно не-volatile читает или пишет, таким образом ограничивая его полезность в качестве межпотокового флага или мьютекса.Более того, не гарантируется, что другие процессоры будут видеть энергозависимые операции чтения и записи в одном и том же порядке из-за кэширования, протокола согласованности кэша и упрощенного упорядочения памяти, а это означает, что одни только изменчивые переменные могут даже не работать как межпоточные флаги или взаимные исключения.1005 *

Так что в общем случае просто volatile не будет работать для C. Но это может работать для некоторых конкретных компиляторов / аппаратных средств и других языков (например, Java 5).

См. Также Является ли функция вызовом барьера памяти?

0 голосов
/ 27 декабря 2011

Не используйте глобальные переменные для достижения своей цели, особенно в многопоточных приложениях !!!Вместо этого используйте Семафор и не делайте Lock, а TryLock.Если TryLock завершается неудачно, это означает, что нет места для другого элемента, поэтому вы можете пропустить его.

Здесь вы можете найти что-то, чтобы прочитать о семафорах в WinAPI, потому что вы, вероятно, используете его: http://msdn.microsoft.com/en-us/library/windows/desktop/ms686946(v=vs.85).aspx
http://msdn.microsoft.com/en-us/library/windows/desktop/ms687032(v=vs.85).aspx

Вы можете достичь функциональности TryLock, передав 0 в качестве тайм-аута функции WaitForSingleObject.

...