Предположим, у нас есть следующий (псевдо) код:
using UpgradeLock = boost::upgrade_lock<boost::shared_mutex>;
using UpgradeToUniqueLock = boost::upgrade_to_unique_lock<boost::shared_mutex>;
boost::shared_mutex mtx;
void DeleteTable(<tbl>) {
UpgradeLock lock(mtx);
if (<the table exists>) {
UpgradeToUniqueLock up(lock); // (!)
// delete the table
}
}
И предположим, что два потока только что вошли в эту функцию и оба достигли утверждения, помеченного (!)
,
Я не могу понять, что будет потом. У меня есть следующие варианты:
UpgradeLock
, но не может, пока не получит эксклюзивный доступ тоже. Тупик.(!)
,Я предполагаю, что второй вариант — это то, что, вероятно, произойдет, но в этом случае выясняется, что, даже заблокировав среду, мы должны перепроверить, что наши данные не были изменены «снаружи», то есть мы должны использовать пресловутый DCLP. Я прав?
Я не нашел правдоподобной информации об этом, так что не вините меня много 🙂
Если я правильно вас понимаю, тогда число 2 произойдет.
Если вы используете Замки для чтения / записи Вы должны придерживаться протокола, который предполагает, что, пока несколько читателей разделяют блокировку, им разрешен только доступ на чтение. Если им нужно получить некоторые права на запись, они должны обновить блокировку.
Теперь в вашем случае может случиться так, что несколько потоков входят в if
-установление и ожидание получения блокировки. Это правда, и все они получат блокировку один за другим. Проблема в том, что сразу после получения блокировки может случиться так, что другой поток уже delete
на столе.
Вот почему вам может понадобиться дважды проверенная схема блокировки.
Отсюда у вас есть несколько вариантов:
Опция 1:
проверьте, указывает ли таблица на NULL
и ничего не делать
вариант 2:
просто позвони delete
опять же, если указатель установлен в NULL
или же nullptr
после звонка delete
C ++ хорошо с этим (если delete
исходит из стандартной библиотеки lib) => и просто ничего не будет делать. Просто для справки читайте:
http://en.cppreference.com/w/cpp/language/delete
если выражение имеет нулевое значение указателя, деструкторы не вызываются, а функция освобождения не вызывается.
UpgradeLock lock(mtx);
if (<the table exists>)
{
UpgradeToUniqueLock up(lock); // (!)
if(<table still exists>)
{
// delete the table
// and set the pointer to nullptr
}
}
но в соответствии с вариантом 2 следующий фрагмент будет просто, если delete
реализуется STL:
Блокировка UpgradeLock (MTX);
if (<the table exists>)
{
UpgradeToUniqueLock up(lock); // (!)
// delete the table
// and set the pointer to nullptr
}
вариант 3:
использование std::shared_ptr
или же boost::shared_ptr
вместо. Они синхронизированы. Так что вам даже не нужны замки, просто позвоните ptr.reset()
из нескольких потоков и все.
Других решений пока нет …