Является ли назначение в системах автоматического подсчета ссылок поточно-ориентированным?

Такие языки, как Swift, Vala и C ++ (через shared_ptr) управляют памятью путем подсчета ссылок. Насколько я знаю, обновления счетчика ссылок в этих системах выполняются атомарно и, таким образом, потокобезопасны.

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

Так делаются ли общие указатели C ++, ссылки на Vala, ссылки Swift, чтобы избежать таких проблем? Если нет, то какие шаги необходимы на каждом из трех языков, чтобы сделать такой доступ безопасным?

Любые идеи приветствуются. Спасибо!

0

Решение

см. последний абзац http://en.cppreference.com/w/cpp/memory/shared_ptr

Все функции-члены (включая конструктор копирования и назначение копирования) могут быть вызваны несколькими потоками в разных экземплярах shared_ptr без дополнительной синхронизации, даже если эти экземпляры являются копиями и имеют общее владение одним и тем же объектом. Если несколько потоков выполнения обращаются к одному и тому же shared_ptr без синхронизации и любой из этих обращений использует неконстантную функцию-член shared_ptr, тогда произойдет гонка данных; перегрузки shared_ptr элементарных функций могут использоваться для предотвращения гонки данных.

shared_ptr переменная не является потокобезопасным и не должен быть доступен из нескольких потоков, если один или несколько потоков изменяют переменная. Несколько переменных, управляющих одним и тем же указателем, являются атомарными, и каждый поток может модифицировать свою собственную копию shared_ptr,

Например, это не безопасно:

#include <iostream>
#include <string>
#include <memory>
#include <vector>
#include <thread>

int main()
{
std::shared_ptr< std::string > str( new std::string() );
std::vector< std::thread > threads;
for ( int i = 0; i < 10; i++ )
{
threads.emplace_back([&]
{
if ( str->empty() )
{
str.reset( new std::string( "thread string" ) );
}
else
{
str.reset();
}
});
}
for ( auto& thread : threads )
{
thread.join();
}
}

но это как темы не изменяют str переменная, но увеличить ее счетчик ссылок:

#include <iostream>
#include <string>
#include <memory>
#include <vector>
#include <thread>

int main()
{
std::shared_ptr< std::string > str( new std::string() );
std::vector< std::thread > threads;
for ( int i = 0; i < 10; i++ )
{
threads.emplace_back([&]
{
std::shared_ptr< std::string > str2 = str;
if ( str2->empty() )
{
str2.reset( new std::string( "thread string" ) );
}
else
{
str2.reset();
}
});
}
for ( auto& thread : threads )
{
thread.join();
}
}

C ++ 20 добавляет std::atomic_shared_ptr что полностью потокобезопасно. До этого вы можете использовать атомарные функции, не являющиеся членами.

4

Другие решения

Подсчет ссылок является поточно-ориентированным в Swift, потому что основной NSObject потокобезопасен. В этом случае счетчик ссылок является неотъемлемым свойством самого объекта, поэтому ваш вопрос спорен. Похоже, то же самое относится и к Вала.

Что оставляет C ++, всегда опаздывает на бал.

std::shared_ptrРеализация подсчет ссылок это потокобезопасно, как ясно показывает цитата из поста Алана, но указывать начать присматривать за другим объектом, очевидно, нет.

Это не принято делать это. Это, скорее, подрывает его назначение, конечно, если вы пытаетесь сделать его более безопасным для работы с кодом. Более подробная информация на cppreference — некоторые из этих перегрузок operator= потокобезопасны, а некоторые нет.

0

По вопросам рекламы [email protected]