Сложный параллельный дизайн - PullRequest
1 голос
/ 21 января 2010

У меня есть класс с именем Root, который служит своего рода телефонной книгой для динамических вызовов методов: он содержит словарь URL-ключей, указывающих на объекты. Когда команда хочет выполнить данный метод, она вызывает экземпляр Root с URL-адресом и некоторым параметром:

root_->call("/some/url", ...);

На самом деле метод вызова в Root выглядит примерно так:

// Version 0
const Value call(const Url &url, const Value &val) {
  // A. find object
  if (!objects_.get(url.path(), &target))
    return ErrorValue(NOT_FOUND_ERROR, url.path());
  }

  // B. trigger the object's method
  return target->trigger(val);
}

Из приведенного выше кода вы можете видеть, что этот метод "call" является не поточно-безопасным: объект "target" может быть удален между A и B, и мы не можем гарантировать, что "objects_" элемент (словарь) не изменяется, пока мы его читаем.

Первое решение, которое пришло мне в голову, было:

// Version I
const Value call(const Url &url, const Value &val) {
  // Lock Root object with a mutex
  ScopedLock lock(mutex_);

  // A. find object
  if (!objects_.get(url.path(), &target))
    return ErrorValue(NOT_FOUND_ERROR, url.path());
  }

  // B. trigger the object's method
  return target->trigger(val);
}

Это нормально, пока "target-> trigger (val)" не является методом, который должен изменять Root, либо путем изменения URL-адреса объекта, либо путем вставки новых объектов. Может помочь изменение области действия и использование мьютекса RW (операций чтения намного больше, чем операций записи в Root):

// Version II
const Value call(const Url &url, const Value &val) {
  // A. find object
  {
    // Use a RW lock with smaller scope
    ScopedRead lock(mutex_);
    if (!objects_.get(url.path(), &target))
      return ErrorValue(NOT_FOUND_ERROR, url.path());
    }
  }
  // ? What happens to 'target' here ?

  // B. trigger the object's method
  return target->trigger(val);
}

Что происходит с «целью»? Как мы можем гарантировать, что он не будет удален между поиском и вызовом?

Некоторые идеи: удаление объекта может быть отложено в очереди сообщений в Root. Но тогда нам понадобится еще одно удаление блокировки чтения мьютекса RW в полной области метода и использование отдельного потока для обработки очереди удаления.

Все это кажется мне очень запутанным, и я не уверен, должен ли параллельный дизайн выглядеть так или у меня просто нет правильных идей.

PS: код является частью проекта с открытым исходным кодом, который называется oscit (OpenSoundControl it).

Ответы [ 2 ]

2 голосов
/ 21 января 2010

Чтобы избежать удаления 'target', мне пришлось написать интеллектуальный указатель с подсчетом потоковых ссылок . Это не так сложно сделать. Единственное, что вам нужно убедиться, это то, что счетчик ссылок доступен в критическом разделе. См. этот пост для получения дополнительной информации.

1 голос
/ 21 января 2010

Вы ошиблись с этим. Имейте в виду: вы не можете заблокировать данные, вы можете только заблокировать код. Вы не можете защитить член "объектов" локально определенным мьютексом. Вам нужен точно такой же мьютекс в коде, который изменяет коллекцию объектов. Он должен блокировать этот код, когда другой поток выполняет метод call (). Мьютекс должен быть определен, по крайней мере, в области видимости класса.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...