между прочим 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
Re: между прочим boost::shared_ptr thread-safe'ный
От: ononim  
Дата: 31.08.09 12:22
Оценка:
R>Он настолько же потоко-безопасный как и std::string. Это называется basic thread-safety, т.е. для одного объекта можно одновременно вызывать константные методы, можно одновременно вызывать не константные методы для разных объектов.
AG>>Было бы интересно на полностью потокобезопасные умные указатели на атоомарных операциях.
А какой смысл в полностью потокобезопасных смартпоинтерах? Если будут работать 2 потока с _одним_ и тем же экземпляром смартпоинтера, даже если он сам по себе будет абсолютно потокобезопасным, сложно себе придумать код, использующий его таким образом, и не требующий синхронизации "поверх". Так как если другой поток вдруг поменяет содержимое смартпоинтера (скормив в reset ему новый инстанс объекта), а первый будет находится в коде обращения к объекту — он с успехом свалится. Чтоб этого избежать потребуется накрывать синхронизацией всю работу с таким смартпоинтеров. Таким образом strong потокобезопаснасть смартпоинтера является ненужным излишеством.

ЗЫ а че форум так расколбасило?
ЗЗЫ а че анонимов обидели?
Как много веселых ребят, и все делают велосипед...
Re[2]: между прочим boost::shared_ptr thread-safe'ный
От: Кодт Россия  
Дата: 31.08.09 12:35
Оценка:
Здравствуйте, ononim, Вы писали:


O>ЗЫ а че форум так расколбасило?

O>ЗЗЫ а че анонимов обидели?

Олег Шастистко с катушек съехал и гадит анонимно через прокси. Пришлось заткнуть.
... << RSDN@Home 1.2.0 alpha 4 rev. 1237>>
Перекуём баги на фичи!
Re[2]: между прочим boost::shared_ptr thread-safe'ный
От: remark Россия http://www.1024cores.net/
Дата: 31.08.09 12:45
Оценка:
Здравствуйте, ononim, Вы писали:

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

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

O>А какой смысл в полностью потокобезопасных смартпоинтерах? Если будут работать 2 потока с _одним_ и тем же экземпляром смартпоинтера, даже если он сам по себе будет абсолютно потокобезопасным, сложно себе придумать код, использующий его таким образом, и не требующий синхронизации "поверх". Так как если другой поток вдруг поменяет содержимое смартпоинтера (скормив в reset ему новый инстанс объекта), а первый будет находится в коде обращения к объекту — он с успехом свалится. Чтоб этого избежать потребуется накрывать синхронизацией всю работу с таким смартпоинтеров. Таким образом strong потокобезопаснасть смартпоинтера является ненужным излишеством.



Хмм... придётся начать с основ. Как работает управление временем жизни на основе подсчёта ссылок: если поток захватывает для себя ссылку на объект, то пока он её не отпустит объект не удаляется... естественно при условии корректной реализации подсчёта ссылок. Соотв. в твоём примере первый поток НЕ свалится, а будут себе преспокойно продолжать работать.

Вот, наверное, основной юз-кейс для указателя со strong thread-safety, заметьте, что он НЕ требует никакой дополнительной внешней синхронизации:
atomic_ptr<settings_t>         g_settings;
atomic_ptr<routing_table_t>    g_routing_table;
atomic_ptr<db_cache_t>         g_db_cache;

void worker_thread()
{
  while (request_t* req = get_next_request())
  {
    local_ptr<settings_t>      settings (g_settings);
    local_ptr<routing_table_t> routing_table (g_routing_table);
    local_ptr<db_cache_t>      db_cache (g_db_cache);
    // process req using settings, routing_table and db_cache
  }
}


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

rw_mutex такой гибкости не даст, он будет требовать полной остановки всех рабочих потоков на время замены объекта, и что возможно более важно — на время остановки потоков. В смысле что потоки будут простаивать не только те 10 машинных инструкций пока непосредственно происходит подмена, а вначале затормозится один поток, потом через 1 мс ещё несколько, а последний поток может вообще сейчас вытеснен и не выполняется, всем остальным придётся подождать пока он будет поставлен на выполнение и наконец не освободит мьютекс. После этого заменяющий поток будет поставлен на выполнение, наконец сделает реальную подмену, и поставит рабочие потоки на выполнение, потом через некоторое время они все вернуться к нормальной работе. В итоге эффективное время простоя может быть существенным.
С другой стороны, конечно, rw_mutex не будет требовать временно хранить 2 копии объекта — текущую и предыдущую.



1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[3]: между прочим boost::shared_ptr thread-safe'ный
От: ononim  
Дата: 31.08.09 14:16
Оценка:
R>Хмм... придётся начать с основ. Как работает управление временем жизни на основе подсчёта ссылок: если поток захватывает для себя ссылку на объект, то пока он её не отпустит объект не удаляется... естественно при условии корректной реализации подсчёта ссылок. Соотв. в твоём примере первый поток НЕ свалится, а будут себе преспокойно продолжать работать.

R>Вот, наверное, основной юз-кейс для указателя со strong thread-safety, заметьте, что он НЕ требует никакой дополнительной внешней синхронизации:

R>
R>atomic_ptr<settings_t>         g_settings;
R>atomic_ptr<routing_table_t>    g_routing_table;
R>atomic_ptr<db_cache_t>         g_db_cache;

R>void worker_thread()
R>{
R>  while (request_t* req = get_next_request())
R>  {
R>    local_ptr<settings_t>      settings (g_settings);
R>    local_ptr<routing_table_t> routing_table (g_routing_table);
R>    local_ptr<db_cache_t>      db_cache (g_db_cache);
R>    // process req using settings, routing_table and db_cache
R>  }
R>}
R>

Это НЕКОРРЕКТНЫЙ пример. Такой код не требует strong thread safety и прекрасно будет работать с boost::shared_ptr. Так что все ваши рассуждения про основы идут лесом.

strong thread safety это если бы:
atomic_ptr<settings_t>         g_settings;
void worker_thread()
{
  while(1)
  {
  settings.reset(new settings_t);
  }
}

работало без проблем. Но стоит попытаться сделать что нить с g_settings — ....
Впрочем можно придумать исключительно академический пример:
atomic_ptr<settings_t>         g_settings;
void worker_thread()
{
  while(1)
  {
  settings.reset(new settings_t);
  atomic_ptr<settings_t> copy = settings;
  copy->foo();
  }
}

но кроме академичности, он ничем не лучше варианта с boost::shared_ptr который написали вы, поставив почемуто там atomic_ptr
Как много веселых ребят, и все делают велосипед...
Re[4]: между прочим boost::shared_ptr thread-safe'ный
От: remark Россия http://www.1024cores.net/
Дата: 31.08.09 14:31
Оценка: +1
Здравствуйте, ononim, Вы писали:

O>Это НЕКОРРЕКТНЫЙ пример. Такой код не требует strong thread safety и прекрасно будет работать с boost::shared_ptr. Так что все ваши рассуждения про основы идут лесом.


Товарищь, ononim, вы бы не спешили рубить с плеча, а лучше бы прочитали дальше:

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


Т.е. часть потоков копирует разделяемый умный указатель, часть делает ему reset(). C boost::shared_ptr<> это работать НЕ будет.



O>strong thread safety это если бы:

O>
O>atomic_ptr<settings_t>         g_settings;
O>void worker_thread()
O>{
O>  while(1)
O>  {
O>  settings.reset(new settings_t);
O>  }
O>}
O>

O>работало без проблем. Но стоит попытаться сделать что нить с g_settings — ....

... всё будет работать нормально.




O>Впрочем можно придумать исключительно академический пример:

O>
O>atomic_ptr<settings_t>         g_settings;
O>void worker_thread()
O>{
O>  while(1)
O>  {
O>  settings.reset(new settings_t);
O>  atomic_ptr<settings_t> copy = settings;
  copy->>foo();
O>  }
O>}
O>

O>но кроме академичности, он ничем не лучше варианта с boost::shared_ptr который написали вы, поставив почемуто там atomic_ptr

Я поставил там atomic_ptr, потому что с boost::shared_ptr код работать не будет.
Мой пример отнюдь не академический, а практический.




1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[5]: между прочим boost::shared_ptr thread-safe'ный
От: ononim  
Дата: 31.08.09 15:48
Оценка:
Здравствуйте, remark, Вы писали:

R>Здравствуйте, ononim, Вы писали:


O>>Это НЕКОРРЕКТНЫЙ пример. Такой код не требует strong thread safety и прекрасно будет работать с boost::shared_ptr. Так что все ваши рассуждения про основы идут лесом.


R>Товарищь, ononim, вы бы не спешили рубить с плеча, а лучше бы прочитали дальше:

R>

R>Параллельно и асинхронно с этим настройки, рутинговая таблица и кэш БД могут изменяться (точнее сказать подменяться на новые). Подмена может происходить с разной частотой, и по разным причиным. Но суть в том, что она нисколько не влияет (не тормозит) основную обработку, обработчики просто захватывают наиболее последнии версии необходимых объектов и производят обработку, используя их, потом отпускают. Когда последняя ссылка на старый объект уходит, то он удаляется.


R>Т.е. часть потоков копирует разделяемый умный указатель, часть делает ему reset(). C boost::shared_ptr<> это работать НЕ будет.

Согласен. Но в вашем коде тут — http://rsdn.ru/forum/cpp/3521564.1.aspx
Автор: remark
Дата: 31.08.09
нету reset для g_settings.

O>>strong thread safety это если бы:

O>>
O>>atomic_ptr<settings_t>         g_settings;
O>>void worker_thread()
O>>{
O>>  while(1)
O>>  {
O>>  settings.reset(new settings_t);
O>>  }
O>>}
O>>

O>>работало без проблем. Но стоит попытаться сделать что нить с g_settings — ....
R>... всё будет работать нормально.
Не будет:

atomic_ptr<settings_t>         settings;
void worker_thread()
{
  while(1)
  {
  settings.reset(new settings_t);
  settings->foo();
  }
}

свалится, несмотря ни на какой супер-пупер-потокобезопасный atomic_ptr, надеюсь не надо объяснять почему.


O>>но кроме академичности, он ничем не лучше варианта с boost::shared_ptr который написали вы, поставив почемуто там atomic_ptr

R>Я поставил там atomic_ptr, потому что с boost::shared_ptr код работать не будет.
Будет. Именно тот код что вы привели в http://rsdn.ru/forum/cpp/3521564.1.aspx
Автор: remark
Дата: 31.08.09
прекраснейшим образом будет работать на boost::shared_ptr.
Как много веселых ребят, и все делают велосипед...
Re[6]: между прочим boost::shared_ptr thread-safe'ный
От: remark Россия http://www.1024cores.net/
Дата: 31.08.09 16:31
Оценка: 3 (1)
Здравствуйте, ononim, Вы писали:

R>>Т.е. часть потоков копирует разделяемый умный указатель, часть делает ему reset(). C boost::shared_ptr<> это работать НЕ будет.

O>Согласен. Но в вашем коде тут — http://rsdn.ru/forum/cpp/3521564.1.aspx
Автор: remark
Дата: 31.08.09
нету reset для g_settings.


Потому что это не код, а пример состоящий из куска кода и текста вокруг него.
Хорошо, вот полный пример:

atomic_ptr<settings_t>         g_settings;
atomic_ptr<routing_table_t>    g_routing_table;
atomic_ptr<db_cache_t>         g_db_cache;

void worker_thread()
{
  while (request_t* req = get_next_request())
  {
    local_ptr<settings_t>      settings (g_settings);
    local_ptr<routing_table_t> routing_table (g_routing_table);
    local_ptr<db_cache_t>      db_cache (g_db_cache);
    // process req using settings, routing_table and db_cache
  }
}

void gui_thread::on_settings_changed_by_user(local_ptr<settings_t> s)
{
  g_settings = s;
}

void routing_table_update_thread()
{
  for (;;)
  {
    local_ptr<routing_table_t> table = recalculate_routing_table();
    g_routing_table = table;
    sleep(100);
  }
}

void db_cache_update_thread()
{
  for (;;)
  {
    local_ptr<db_cache_t> cache = local_from_db();
    g_db_cache = cache;
    sleep(10000);
  }
}



R>>... всё будет работать нормально.

O>Не будет:

O>
O>atomic_ptr<settings_t>         settings;
O>void worker_thread()
O>{
O>  while(1)
O>  {
O>  settings.reset(new settings_t);
O>  settings->foo();
O>  }
O>}
O>

O>свалится, несмотря ни на какой супер-пупер-потокобезопасный atomic_ptr, надеюсь не надо объяснять почему.


Любая автоматическая система управлением временем жизни не удаляет объекты пока они используются. Точка.
Если происходит обратное, то либо (1) в системе есть банальная ошибка, (2) её некорректно используют.

По поводу выше-обозначенного примера нельзя определенно сказать, пока мы не определимся о каком именно atomic_ptr идёт речь. Но я могу предположить 3 варианта:
1. atomic_ptr поддерживает прямые обращения к объекту. Соотв. operator->() возвращает local_ptr, который держит объект, пока не выполнится foo(). Диагноз: всё прекрасно работает.
2. atomic_ptr просто не поддерживает прямые обращения к объекту, и заставляет пользователя вначале явно создавать local_ptr. Диагноз: такой код не скомпилируется.
3. atomic_ptr криво спроектирован и/или реализован. Диагноз: при таком предположении любой код может валиться.



O>>>но кроме академичности, он ничем не лучше варианта с boost::shared_ptr который написали вы, поставив почемуто там atomic_ptr

R>>Я поставил там atomic_ptr, потому что с boost::shared_ptr код работать не будет.
O>Будет. Именно тот код что вы привели в http://rsdn.ru/forum/cpp/3521564.1.aspx
Автор: remark
Дата: 31.08.09
прекраснейшим образом будет работать на boost::shared_ptr.


Хорошо, считаем, что произошло некоторое недопонимание из-за того, что я часть кода описал словами. Полный код вверху этого поста.
Теперь вопрос по поводу того, что подсчёт ссылок с сильной потоко-безопасностью пустое излишество, и что при использовании смарт поинтеров нужна дополнительная синхронизацией снимается?



1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re: между прочим boost::shared_ptr thread-safe'ный
От: remark Россия http://www.1024cores.net/
Дата: 31.08.09 16:40
Оценка: 6 (1)
Здравствуйте, remark, Вы писали:

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


На всякий случай упомяну, что "не на атомарных операциях" это решается как boost::shared_ptr + boost::mutex в одном флаконе. Т.е. тупо все операции над умным указателем защищаем мьютексом.

И ещё сюда же, С++0х std::shared_ptr будет поддерживать сильную потоко-безопасность. Насколько помню через перегрузку функций std::atomic_load(). Однако тип указателя один и для разделяемых переменных и для локальных переменных.



1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[7]: между прочим boost::shared_ptr thread-safe'ный
От: ononim  
Дата: 31.08.09 17:46
Оценка: -1 :)
R>>>Т.е. часть потоков копирует разделяемый умный указатель, часть делает ему reset(). C boost::shared_ptr<> это работать НЕ будет.
O>>Согласен. Но в вашем коде тут — http://rsdn.ru/forum/cpp/3521564.1.aspx
Автор: remark
Дата: 31.08.09
нету reset для g_settings.

R>Потому что это не код, а пример состоящий из куска кода и текста вокруг него.
R>Хорошо, вот полный пример:
Совсем некрасиво менять концептуально код, ссылаясь на то что мол "изначально мелкие детали опустил", причем новый вариант явно перегружая действительно мелкими деталями, помимо важной смены концепции.


R>>>... всё будет работать нормально.

O>>Не будет:

O>>
O>>atomic_ptr<settings_t>         settings;
O>>void worker_thread()
O>>{
O>>  while(1)
O>>  {
O>>  settings.reset(new settings_t);
O>>  settings->foo();
O>>  }
O>>}
O>>

O>>свалится, несмотря ни на какой супер-пупер-потокобезопасный atomic_ptr, надеюсь не надо объяснять почему.


R>Любая автоматическая система управлением временем жизни не удаляет объекты пока они используются. Точка.

R>Если происходит обратное, то либо (1) в системе есть банальная ошибка, (2) её некорректно используют.

R>По поводу выше-обозначенного примера нельзя определенно сказать, пока мы не определимся о каком именно atomic_ptr идёт речь. Но я могу предположить 3 варианта:

R>1. atomic_ptr поддерживает прямые обращения к объекту. Соотв. operator->() возвращает local_ptr, который держит объект, пока не выполнится foo(). Диагноз: всё прекрасно работает.
Замечательно, но не скомпилируется без явного тайпкаста от local_ptr к settings_t. Иначе откуда у local_ptr вырастет foo()? А если придется писать явные тайпасты при каждом вызове (представляете себе продакшн код?), либо модифицировать оборачиваемый класс (boost::intrusive_ptr тоже самое делает лучшим образом, позволяя реализовать и strong thread safety, благо там и синхронизировать то нечего) — это и есть академичность. Ну может моих познаний в С++ недостаточно чтобы сделать такой local_ptr. В таком случае просветите, и если оно скомпиляется — честно признаю поражение


R>2. atomic_ptr просто не поддерживает прямые обращения к объекту, и заставляет пользователя вначале явно создавать local_ptr. Диагноз: такой код не скомпилируется.

Вобщем-то это компилябельный вариант пункта 1

R>3. atomic_ptr криво спроектирован и/или реализован. Диагноз: при таком предположении любой код может валиться.

Это верно, но см 1.
Как много веселых ребят, и все делают велосипед...
Re[8]: между прочим boost::shared_ptr thread-safe'ный
От: Николай Ивченков  
Дата: 31.08.09 18:23
Оценка: 15 (1)
ononim:

R>>1. atomic_ptr поддерживает прямые обращения к объекту. Соотв. operator->() возвращает local_ptr, который держит объект, пока не выполнится foo(). Диагноз: всё прекрасно работает.

O>Замечательно, но не скомпилируется без явного тайпкаста от local_ptr к settings_t. Иначе откуда у local_ptr вырастет foo()?

Со стрелкой-то как раз всё очень просто:

template <class T>
class local_ptr;

template <class T>
class atomic_ptr
{
public:
    explicit atomic_ptr(T *p) /* ... */ { /* ... */ }
    local_ptr<T> operator ->();
    // ...
};

template <class T>
class local_ptr
{
public:
    template <class Tx>
    local_ptr(atomic_ptr<Tx>) /* ... */ { /* ... */ }
    
    T *operator ->() { return m_p; }
    // ...
private:
    T *m_p;
    // ...
};

template <class T>
local_ptr<T> atomic_ptr<T>::operator ->()
    { return local_ptr<T>(*this); }
    
struct X
{
    void f() {}
};    
    
int main()
{
    atomic_ptr<X> p(new X);
    p->f();
}

А вот сделать нормальное разыменование через * не получится. Понадобятся костылики.
Re[9]: между прочим boost::shared_ptr thread-safe'ный
От: ononim  
Дата: 31.08.09 18:42
Оценка:
R>>>1. atomic_ptr поддерживает прямые обращения к объекту. Соотв. operator->() возвращает local_ptr, который держит объект, пока не выполнится foo(). Диагноз: всё прекрасно работает.
O>>Замечательно, но не скомпилируется без явного тайпкаста от local_ptr к settings_t. Иначе откуда у local_ptr вырастет foo()?
НИ>Со стрелкой-то как раз всё очень просто:
ну чтож, согласен
Как много веселых ребят, и все делают велосипед...
Re[8]: между прочим boost::shared_ptr thread-safe'ный
От: remark Россия http://www.1024cores.net/
Дата: 31.08.09 18:45
Оценка:
Здравствуйте, ononim, Вы писали:

R>>>>Т.е. часть потоков копирует разделяемый умный указатель, часть делает ему reset(). C boost::shared_ptr<> это работать НЕ будет.

O>>>Согласен. Но в вашем коде тут — http://rsdn.ru/forum/cpp/3521564.1.aspx
Автор: remark
Дата: 31.08.09
нету reset для g_settings.

R>>Потому что это не код, а пример состоящий из куска кода и текста вокруг него.
R>>Хорошо, вот полный пример:

O>Совсем некрасиво менять концептуально код, ссылаясь на то что мол "изначально мелкие детали опустил", причем новый вариант явно перегружая действительно мелкими деталями, помимо важной смены концепции.


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



R>>Любая автоматическая система управлением временем жизни не удаляет объекты пока они используются. Точка.

R>>Если происходит обратное, то либо (1) в системе есть банальная ошибка, (2) её некорректно используют.

R>>По поводу выше-обозначенного примера нельзя определенно сказать, пока мы не определимся о каком именно atomic_ptr идёт речь. Но я могу предположить 3 варианта:

R>>1. atomic_ptr поддерживает прямые обращения к объекту. Соотв. operator->() возвращает local_ptr, который держит объект, пока не выполнится foo(). Диагноз: всё прекрасно работает.
O>Замечательно, но не скомпилируется без явного тайпкаста от local_ptr к settings_t. Иначе откуда у local_ptr вырастет foo()? А если придется писать явные тайпасты при каждом вызове (представляете себе продакшн код?), либо модифицировать оборачиваемый класс (boost::intrusive_ptr тоже самое делает лучшим образом, позволяя реализовать и strong thread safety, благо там и синхронизировать то нечего) — это и есть академичность. Ну может моих познаний в С++ недостаточно чтобы сделать такой local_ptr. В таком случае просветите, и если оно скомпиляется — честно признаю поражение

Если речь уже идёт о тонкостях и трюках С++, то предлагаю выносить в отдельную ветку. Я думаю, что тут достаточно людей смогут показать, как это сделать.


ononim,
Я, честно говоря, уже начинаю терять суть дискуссии.
Мой единственный тезис в этой ветке, что "Таким образом strong потокобезопаснасть смартпоинтера является ненужным излишеством" — это полный бред и неправда. Если не согласны, то я готов парировать конкретные аргументы.



1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re: между прочим boost::shared_ptr thread-safe'ный
От: D14  
Дата: 03.09.09 20:18
Оценка:
Здравствуйте, remark, Вы писали:

R>"Полностью" потоко-безопасные указатели (для которых можно одновременно делать reset() и get()) называются with strong thread-safety.

R>Ты наверное имел в виду не на атомарных операциях, а lock/wait-free. Потому что на атомарных операциях легко сымитировать спин-мьютекс, и дальше...
R>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), т.е. локальный для потока.

А два счётчика принципиально? Вроде бы QSharedPointer из Qt обещает быть thread-safe. Там два или не два?
Re[2]: между прочим boost::shared_ptr thread-safe'ный
От: remark Россия http://www.1024cores.net/
Дата: 04.09.09 09:16
Оценка:
Здравствуйте, D14, Вы писали:

D14>А два счётчика принципиально? Вроде бы QSharedPointer из Qt обещает быть thread-safe. Там два или не два?


Там один (ну точнее 2, но по другой причине — они поддерживают слабые ссылки, но это совсем другая история, для наших целей можно считать, что один).
Соотв. QSharedPointer ни разу не thread-safe, такой же thread-safe как и std::string.

А документация просто полная чушь:

QSharedPointer and QWeakPointer are thread-safe and operate atomically on the pointer value. Different threads can also access the same QSharedPointer or QWeakPointer object at the same time without need for locking mechanisms.


Смотри код:
        inline void ref() const { d->weakref.ref(); d->strongref.ref(); }

        inline void internalSet(Data *o, T *actual)
        {
            if (d == o) return;
            if (o && !o->strongref)
                o = 0;
            if (o) {
                verifyReconstruction(actual);
                o->weakref.ref();
                o->strongref.ref();
            }
            if (d && !deref())
                delete d;
            d = o;
            this->value = d && d->strongref ? actual : 0;
        }


Это тот же самый примитивный basic-thread safety atomic reference counting, что и в boost::shared_ptr.
А теперь представь, что один поток зашёл в ref(), считал значение d, и был прерван.
Пришёл второй поток, вызвал internalSet(), заменил объект на новый, а старый удалил.
Теперь просыпается первый поток, и пытается своему, уже удалённому, объекту вызвать d->weakref.ref().



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