Задача синхронизации на вариации классической задачи ограниченного буфера с использованием Java - PullRequest
0 голосов
/ 05 апреля 2011

Я сделал вариант классической задачи с ограниченным буфером.

Это ОЧЕНЬ отличается, и на дороге есть небольшой технический нюанс, который мне нужно знать для обеспечения безопасности потоков (вместо того, чтобы бесконечно проверять состояние, которое МОЖЕТ никогда не происходить)

Не думаю, что мне нужно объяснять ситуацию, речь идет об оценке следующего примера кода

if(buffer1.BufferNotFull)
{
    buffer1.Lock();
    buffer1.AddValueAtIndex(value, index);
    buffer1.Unlock();
}

В основном мой buffer1 имеет простую переменную условия (обновленную и все), которая позволяет мне узнать, не заполнена ли она, и затем я получаю блокировку, выполняю магию и освобождаю блокировку.

Но могу ли я просто предположить, что оценка условия "buffernotfull" не может чередовать с другим потоком, просто записывающим через LAST-слот в моем буфере?

(буфер на самом деле представляет собой массив значений с -1, представляющий пустое, не спрашивайте, почему ^ _ ^)

Вкратце: оценка

if(buffer1.BufferNotFull)

потокобезопасный с кодом тела, запрашивающим блокировку

EDIT

Это система, которую я сейчас использую для всей моей системы. Я надеюсь, что это помогает показать мой дизайн. Примите во внимание, что я сделал этот проект исключительно для изучения этих основных механизмов синхронизации.

Поток управления для потоков, обращающихся к буферам

Чтение входного потока (синхронизация не требуется)

ПОПЫТКА:

  1. Операция записи из потока производителя в буфере1 ЗАПРОС ЗАПРОСА:
  2. Если блокировка запрещена, продолжайте запрашивать блокировка.
  3. Если переменная условия не выполняется, ждать
  4. Если блокировка принята, выполнить запись работа
  5. разблокировать buffer1 и уведомить других ожидающие темы.
  6. Если входной поток пуст и buffer1 умереть и просить смерти от filterthread

ПОПЫТКА:

  1. Операция чтения из фильтра поток в буфере1 ЗАПРОС ЗАПРОСА:
  2. Если блокировка запрещена, продолжайте запрашивать блокировку
  3. Если блокировка принята, выполнить чтение операция (замените значение на -1) и сохранить значение в потоке после применения фильтра.
  4. Если переменная условия не выполняется, ждать.
  5. разблокировать buffer1 и уведомить других ожидающие темы.
  6. Если buffer1 и buffer2 пусты и запрос на смерть существует, умри.

ПОПЫТКА:

  1. Операция записи из фильтра поток в буфере2 ЗАПРОС ЗАПРОСА:
  2. Если блокировка запрещена, продолжайте запрашивать блокировку
  3. Если блокировка принята, выполнить запись операция (с отфильтрованным результаты).
  4. Если переменная условия не выполняется, ждать.
  5. разблокировать буфер2 и уведомить других ожидающие темы.
  6. Если buffer1 и buffer2 пусты и запрос о смерти существует, отправить смерть просьба потребителя нарезать и умереть.

ПОПЫТКА:

  1. Операция чтения из буфера2 из потребительская нить
  2. Если блокировка запрещена, продолжайте запрашивать блокировку
  3. Если блокировка принята, выполнить чтение операция (замените значение на -1) и напрямую выводить переменную на консоль.
  4. Если переменная условия не выполняется, ждать.
  5. Разблокировать buffer2 и уведомить других ожидающие темы.
  6. Если buffer2 пуст и смерть запрос существует, умри.

Поток управления для отдельных операций резьбы

получить блокировку Проверьте переменную условия Если ДА, операция записи / чтения Если НЕТ, подождите Разблокировка

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

1 Ответ

3 голосов
/ 05 апреля 2011

Обычно в приведенном выше сценарии вы должны сначала получить блокировку, а затем проверить, чтобы условие было выполнено:

buffer1.Lock();

try {

    while( !buffer1.BufferNotFull ) {

          buffer1.WaitOnConditionVariable();
    }

    buffer1.AddElement(...);

} finally {

    buffer1.Unlock();
}

Любой другой порядок операций накладывает риск состояния гонки «время проверки / время использования»: рассмотрим сценарий, в котором текущий поток прерывается сразу после проверки состояния с помощью BufferNotFull, но прежде чем захватить замок. Если новый поток добавляет новый элемент в буфер, состояние буфера может снова стать «полным». Когда исходный поток возобновляет работу, он, таким образом, вызовет исключение «заполнение буфера» (или что-то еще) при попытке добавить собственный новый элемент, даже если он правильно проверил (с его собственной точки зрения).

Редактировать Переменная условия снимет блокировку до того, как она фактически перейдет в режим гибернации во время операции ожидания. Это делается атомарно, то есть поток будет спать «в одно и то же мгновение», когда снимается блокировка. Когда переменная уведомляется другим потоком, она повторно получает блокировку, прежде чем возобновит выполнение после вызова «WaitOnConditionVariable».

Код, удаляющий элементы из буфера, может выглядеть так:

buffer1.Lock();

try {

    if( !buffer.IsEmpty ) { 

         ... remove element ...
         BufferNotFull = true;
         NotifyConditionVariable();
    }
} finally {
    buffer1.Unlock();
}

Или, чтобы он действительно выглядел как Java:

Object monitor = new Object();
boolean full = false;
boolean empty = true;

Добавление элементов:

synchronized( monitor ) {
    while( full ) {
        monitor.wait();
    }

    ... add element ...
    ... maybe set full ...
    empty = false;
    monitor.notifyAll();
}

Извлечение элементов:

synchronized( monitor ) {
    while( empty ) monitor.wait();

    ... remove element ...
    ... maybe set empty ...
    full = false;
    monitor.notifyAll();
}
...