Подтвердить что ты не робот

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

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

Скажем, у меня есть объект библиотеки:

dbLib::SomeObject someObject;

Сейчас я могу сделать что-то вроде этого:

dbLib::ErrorCode errorCode = 0;
std::list<dbLib::Item> items;
{
    DbLock dbLock;
    errorCode = someObject.someFunction(&items);
} // dbLock goes out of scope

Я хотел бы упростить это к чему-то вроде этого (или даже проще):

dbLib::ErrorCode errorCode =
    protectedCall(someObject, &dbLib::SomeObject::someFunction(&items));

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

Я уверен, что это общий шаблон/идиома, но я не знаю его имени или каких ключевых слов для поиска. (Глядя на http://www.vincehuston.org/dp/gof_intents.html Я думаю, это скорее идиома, чем шаблон).

Где я должен искать дополнительную информацию?

4b9b3361

Ответ 1

Вы можете сделать protectedCall функцией шаблона, которая принимает функтор без аргументов (что означает, что вы привязываете аргументы на сайте вызова), а затем создает блокировку с привязкой, вызывает функтор и возвращает его значение. Например, что-то вроде:

template <typename Ret>
Ret protectedCall(boost::function<Ret ()> func)
{
    DbLock lock;
    return func();
}

Вы бы назвали это следующим образом:

dbLib::ErrorCode errorCode = protectedCall(boost::bind(&dbLib::SomeObject::someFunction, &items));

EDIT. Если вы используете С++ 0x, вы можете использовать std::function и std::bind вместо эквивалентов boost.

Ответ 2

В С++ 0x вы можете реализовать некоторую форму декораторов:

template <typename F>
auto protect(F&& f) -> decltype(f())
{
    DbLock lock;
    return f();
}

использование:

dbLib::ErrorCode errorCode = protect([&]() 
{
    return someObject.someFunction(&items); 
});

Ответ 3

Из вашего описания это выглядит как работа Decorator Pattern.

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

Причина в том, что в целом эти функции имеют тенденцию к серьезному ухудшению, требуют более высокой (менее мелкой) блокировки для согласованности или возвращают ссылки на внутренние структуры, которые требуют блокировки для блокировки до тех пор, пока не будет прочитана вся информация.

Подумайте, например. о функции БД, которая вызывает хранимую процедуру, которая возвращает BLOB (поток) или курсор ref: потоки не должны считываться за пределами блокировки.

Что делать?

Я рекомендую вместо этого использовать Facade Pattern. Вместо того, чтобы составлять ваши операции непосредственно в терминах вызовов БД, реализуйте фасад, который использует уровень БД; Затем этот уровень мог бы управлять блокировкой на точно требуемом уровне (и оптимизировать там, где необходимо: вы могли бы реализовать фасад в качестве потокового локального Singleton и использовать отдельные ресурсы, устраняя необходимость блокировок, например.)

Ответ 4

Простейшим (и все же простым) решением может быть запись функции, которая возвращает прокси для объекта. Прокси-сервер выполняет блокировку и перегрузки → , чтобы разрешить вызов объекта. Вот пример:

#include <cstdio>

template<class T>
 class call_proxy 
 {
  T &item; 
  public:

  call_proxy(T &t) : item(t) { puts("LOCK"); }
  T *operator -> () { return &item; }
  ~call_proxy() { puts("UNLOCK"); }
 };

template<class T>
call_proxy<T> protect(T &t) 
{    
 return call_proxy<T>(t);
}

Здесь, как его использовать:

class Intf
{
 public:
  void function() 
 {
  puts("foo");
 }
};

int main()
{
 Intf a; 
 protect(a)->function(); 
}

Выход должен быть:

LOCK
foo
UNLOCK

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

#define PCALL(X,APPL) (protect(X), (X).APPL)
PCALL(x,x.function());

Это дважды оценивает x.

Ответ 5

В этой статье от Andrei Alexandrescu есть довольно интересная статья о том, как создать такую ​​тонкую оболочку и объединить ее с страшным волатильным ключевым словом для обеспечения безопасности потоков.

Ответ 6

Блокировка Mutex - аналогичная проблема. Он попросил о помощи здесь: Нужна некоторая обратная связь о том, как сделать класс "потокобезопасным"

Решение, с которым я столкнулся, было классом-оболочкой, который предотвращает доступ к защищенному объекту. Доступ можно получить через класс "accessor". Аксессор заблокирует мьютекс в своем конструкторе и разблокирует его при уничтожении. Подробнее см. В разделах "ThreadSafe" и "Locker" в Threading.h.