Написание класса C ++, который может использоваться как статический, но нуждается в блокировке - PullRequest
6 голосов
/ 26 ноября 2011

Мне нужно написать класс, который загружает общие библиотеки. Последовательность dlopen () / dlerror () нуждается в блокировке для обеспечения безопасности потока.

class LibLoader {
  public:
  LibLoader(string whichLib);
  bool Load() { Wait(lock); ... dlopen() ... dlerror() ... }
  bool Unload() { Wait(lock); ... dlclose() ... dlerror() ... }
  bool IsLoaded() {...}
  // ... access to symbols...
  private:
  static Lock lock;
}
Lock Lock::lock;

Пользователи этого класса (их будет одновременно несколько) захотят сделать его статическим членом этого класса, чтобы избежать многократной загрузки общей библиотеки для каждого объекта класса:

class NeedsALib {
public:
NeedsALib() { if (!myLib.IsLoaded()) { myLib.Load(); } }
private:
static LibLoader myLib;
}
LibLoader::myLib;

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

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

Ответы [ 2 ]

3 голосов
/ 26 ноября 2011

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

  1. Что произойдет, если два потока впервые получат доступ к блокировке?[то есть, вы не можете лениво создать блокировку]
  2. Что произойдет, если блокировка будет разрушена слишком рано?[то есть вы не можете статически создать блокировку]

Комбинация этих ограничений заставляет вас использовать непереносимый механизм для создания блокировки.

На pthreads наиболееПростой способ справиться с этим - PTHREAD_MUTEX_INITIALIZER, который позволяет статически инициализировать блокировки:

class LibLoader{
  static pthread_mutex_t mutex;
// ...
};

// never destroyed
pthread_mutex_t LibLoader::mutex = PTHREAD_MUTEX_INITIALIZER;

В Windows вы можете использовать синхронную однократную инициализацию .

В качестве альтернативы, если вы можете гарантировать, что перед основными запусками будет только один поток, вы можете использовать шаблон синглтона без разрушения и просто принудительно коснуться блокировки перед main ():

class LibLoader {
  class init_helper {
    init_helper() { LibLoader::getLock(); }
  };

  static init_helper _ih;
  static Lock *_theLock;

  static Lock *getLock() {
    if (!_theLock)
      _theLock = new Lock();
    return _theLock;
  }
  // ...
};

static init_helper LibLoader::_ih;
static Lock *LibLoader::_theLock;

Обратите внимание, что это делает, возможно, непереносимым (но весьма вероятным истинным) предположение о том, что статические объекты типа POD не уничтожаются до тех пор, пока не будут уничтожены все не-POD статические объекты.Я не знаю ни одной платформы, в которой это не так.

1 голос
/ 26 ноября 2011

Обобщение требований: требуется несколько LibLoader экземпляров, каждый для своей библиотеки, но должна существовать одна блокировка, чтобы они не перезаписывали коды ошибок друг друга.

Одним из способов было бы положиться на статический порядок инициализации и уничтожения в файле.

Лучше было бы не создавать LibLoader статического поля в NeedsALib (и т. П.). Кажется, эти клиентские классы могут быть переданы экземпляру права LibLoader в конструкторе.

Если создание экземпляров LibLoader вне его клиентских классов не удобно, вы можете сделать все статические поля (блокировка и загрузчики) указателями и использовать шаблон синглтона с отложенной инициализацией. Затем, когда вы создаете первый загрузчик, он также создает блокировку. Синглтон сам потребовал бы блокировки здесь, но вы могли бы запустить его до порождения своих потоков. Уничтожение также будет явным и под вашим контролем. Вы также можете сделать это только с загрузчиками (сохраняя статическую блокировку).

Кроме того, если LibLoader не имеет большого количества состояний для хранения, вы можете настроить каждый клиентский класс (NeedsALib и т. Д.) Для создания своего собственного LibLoader. Это, правда, довольно расточительно.

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