между прочим boost::shared_ptr thread-safe'ный
От: remark Россия http://www.1024cores.net/
Дата: 31.08.09 10:44
Оценка: 6 (1)
Здравствуйте, Alexander G, Вы писали:

AG>Между прочим, в узком смысле. Одновременный reset и get из разных нитей недопустим.



Он настолько же потоко-безопасный как и std::string. Это называется basic thread-safety, т.е. для одного объекта можно одновременно вызывать константные методы, можно одновременно вызывать не константные методы для разных объектов.


AG>Было бы интересно на полностью потокобезопасные умные указатели на атоомарных операциях.



"Полностью" потоко-безопасные указатели (для которых можно одновременно делать reset() и get()) называются with strong thread-safety.
Ты наверное имел в виду не на атомарных операциях, а lock/wait-free. Потому что на атомарных операциях легко сымитировать спин-мьютекс, и дальше...
lock/wait-free алгоритмы (точнее алгоритм, по большому счёту он один) подсчёта ссылок со strong thread-safety основаны на т.н. дифференциальном подсчёте ссылок. Т.е. есть 2 счётчика: первый — в объекте как обычно, а второй — внутри умного указателя рядом с указателем на объект. На первый идут операции декремента, а на второй — операции инкремента. Когда умный указатель разрушается или изменяет значение его счётчик переносится в объект. Соотв. тут уже получается 2 типа умных указателей — условно atomic_ptr и shared_ptr (или более правильно было бы назвать их shared_ptr и local_ptr), у первого можно любые операции одновременно дёргать, т.е. подразумевается, что он лежит в каком-то разделяемом общедоступном месте (допустим глобальная переменная atomic_ptr<settings_t> g_current_settings). Второй — как обычный std::string (или boost::shared_ptr), т.е. локальный для потока.

Основная хитрость в том, что когда поток захватывает разделяемый указатель он делает это атомарно (одновременно увеличивает счётчик и считывает значение указателя, для которого он увеличил счётчик):
struct atomic_ptr
{
  T* obj;
  unsigned cntr;
};

T* acquire(atomic_ptr* ptr)
{
  atomic_ptr tmp = ATOMIC_FETCH_ADD(ptr, 1 << 32);
  return tmp.obj;
}


Когда разделяемый указатель изменяется, то это тоже происходит атомарно за одну операцию:
void reset(atomic_ptr* ptr, T* n)
{
  atomic_ptr xchg = {n, 0};
  atomic_ptr tmp = ATOMIC_XCHG(ptr, xchg);
  // some manipulations with tmp to correctly handle
  // reference count for previous object
}



Первая (и насколько мне известно единственная) реализация алгоритма в виде полноценных умных указателей — это atomic-ptr-plus:
http://atomic-ptr-plus.sourceforge.net/

Несколько лет назад я выкладывал тут компонент, который использует схожий, но несколько улучшенный алгоритм (он wait-free, в отличии от atomic-ptr-plus, хотя там есть свои тонкости). Фактически это умный указатель, только сильно ограниченный в использовании: может быть только один разделяемый указатель на объект, локальные указатели нельзя копировать. Т.е. он предназначен именно для ситуации "atomic_ptr<settings_t> g_current_settings", а рабочие потоки захватывают текущие настройки на время обработки транзакции, работают с ними, потом отпускают; а глобальная переменная g_current_settings может в любой момент изменяться.
Тут Store — это есть atomic_ptr, а Handle — shared_ptr:
http://rsdn.ru/forum/cpp/1922573.1.aspx
Автор: remark
Дата: 28.05.06


Занятный момент, что в современных языках со сборкой мусора (Java, C#), обычные "указатели" (объектные ссылки) полностью атомарные. Т.е. этой проблемы вообще нет, пожалуйста размещай указатель в разделяемом месте, одни потоки будут его периодически менять, другие считывать. Это будет замечательно работать, и очень дёшево: что захват, что изменение указателя — обычные инструкции загрузки/сохранения.




31.08.09 15:27: Ветка выделена из темы между прочим boost::shared_ptr thread-safe'ный
Автор: makes
Дата: 29.08.09
— Кодт

1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.