自动引用计数系统中的赋值是否是线程安全的?

时间:2018-06-07 06:46:47

标签: c++ swift multithreading vala reference-counting

Swift,Vala和C ++等语言(通过shared_ptr)通过引用计数来管理内存。据我所知,这些系统中引用计数的更新是以原子方式执行的,因此是线程安全的。

但是,每次重新分配引用/指针时,前引用的对象需要引用计数减量,新引用的对象需要引用增量,最后必须重新分配引用本身。因此,如果可以从多个线程(即通过全局变量)访问相同的引用,并且同时由多个线程重新分配,则引用计数可能会变为乱码。

C ++共享指针,Vala引用,Swift引用是否采取措施避免此类问题?如果不是这三种语言中的每种语言都需要哪些步骤才能使这种访问安全?

任何见解都表示赞赏。谢谢!

2 个答案:

答案 0 :(得分:4)

请参阅http://en.cppreference.com/w/cpp/memory/shared_ptr

的最后一段
  

所有成员函数(包括复制构造函数和复制赋值)都可以由shared_ptr的不同实例上的多个线程调用,而无需其他同步,即使这些实例是副本并共享同一对象的所有权。如果多个执行线程在没有同步的情况下访问相同的shared_ptr,并且这些访问中的任何一个使用shared_ptr的非const成员函数,那么将发生数据争用;原子函数的shared_ptr重载可用于防止数据竞争。

shared_ptr 变量 不是线程安全的,如果一个或多个线程修改 <,则不应从多个线程访问EM>可变 即可。管理同一指针的多个变量是原子的,每个线程都可以自由修改它自己的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,它完全是线程安全的。在此之前,您可以使用原子非成员函数。

答案 1 :(得分:0)

引用计数是Swift中的线程安全,因为底层NSObject是线程安全的。在这种情况下,引用计数是对象本身的固有属性,因此您的问题没有实际意义。 Vala看起来也是如此。

离开C ++,总是迟到了。

std::shared_ptr 引用计数的实现是线程安全的,正如Alan的帖子中的引用清楚,但告诉一个人开始照看另一个对象显然不是。

这样做并不常见。这相当破坏了它的目的,当然如果你正在尝试使你的代码线程安全。 cppreference处的更多详细信息 - operator=的一些重载是线程安全的,有些则不是。