具有shared_ptr的多线程

Multithreading with shared_ptr

本文关键字:多线程 ptr shared 具有      更新时间:2023-10-16

我去塞我的简单多线程应用程序,我将其用作简单的测试台。我想要实现的是修改传递给多个线程的一个变量的值,并在全部完成后读取结果。

#include <iostream>
#include <memory>
#include <thread>
#include <mutex>
void updateValue(const std::shared_ptr<int>& value);
int main()
{
    auto sp = std::make_shared<int>(0);
    std::thread incrementThread_1(updateValue,sp);
    std::thread incrementThread_2(updateValue,sp);
    incrementThread_1.join();
    incrementThread_2.join();
    std::cout << *sp << std::endl;
}
void updateValue(const std::shared_ptr<int>& value)
{
    std::mutex g_i_mutex;
    for(int i = 0; i<100;i++)
    {
        std::unique_lock<std::mutex> lk(g_i_mutex);
        (*value)++;
        lk.unlock();
    }
}

目前它只是崩溃。我在调试窗口中没有任何有意义的信息,所以它也无济于事。有人可以告诉我我可能做错了什么吗?

需要指出的一件事 - 我不想使用全局变量,因为这些函数最初位于不同的文件中。

线程之间共享的数据需要提供自己的互斥锁(与完全相同的线程集共享),并要求所有访问器同意使用该互斥锁序列化访问。

典型的解决方案是提供自定义类型来封装此责任。使用此方法,代码将大致如下所示:

struct SyncInt
{
    int n = 0;
    std::mutex mx;
};
void updateValue(SyncInt & value)
{
    for (int i = 0; i != 100; ++i)
    {
        std::lock_guard<std::mutex> lock(value.mx);
        ++value.n;
    }
}
int main()
{
    SyncInt v;
    std::thread t1(updateValue, std::ref(v)), t2(updateValue, std::ref(v));
    t1.join();
    t2.join();
    std::cout << v.n << "n";
}

(请注意,示例中的共享指针对于有关并发性的问题并不重要,因为您的所有代码都只使用点,而从未使用指针本身。

目前它只是崩溃。有人可以告诉我我可能会做什么吗 错?

您正在从 2 个不同的线程更新共享变量value,实际上没有任何同步。这是数据争用条件,即未定义的行为。修复它的最简单方法是使g_i_mutex全局化:

#include <iostream>
#include <memory>
#include <thread>
#include <mutex>
std::mutex g_i_mutex;
void updateValue(const std::shared_ptr<int>& value);
int main()
{
    auto sp = std::make_shared<int>(0);
    std::thread incrementThread_1(updateValue,sp);
    std::thread incrementThread_2(updateValue,sp);
    incrementThread_1.join();
    incrementThread_2.join();
    std::cout << *sp << std::endl;
}
void updateValue(const std::shared_ptr<int>& value)
{
    for(int i = 0; i<100;i++)
    {
        std::unique_lock<std::mutex> lk(g_i_mutex);
        (*value)++;
        lk.unlock();
    }
}

另一个没有全局变量的解决方案:

#include <iostream>
#include <memory>
#include <thread>
#include <mutex>
void updateValue(const std::shared_ptr<int>& value, std::mutex& g_i_mutex)
{
    for(int i = 0; i<100;i++)
    {
        std::unique_lock<std::mutex> lk(g_i_mutex);
        (*value)++;
        lk.unlock();
    }
}
int main()
{
    auto sp = std::make_shared<int>(0);
    std::mutex g_i_mutex;
    std::thread incrementThread_1(updateValue, sp, std::ref(g_i_mutex));
    std::thread incrementThread_2(updateValue, sp, std::ref(g_i_mutex));
    incrementThread_1.join();
    incrementThread_2.join();
    std::cout << *sp << std::endl;
}

关于这个问题的优点,在这种情况下根本不需要使用shared_ptr。您所需要的只是atomic<int>

您希望使用这样的数据结构,它将互斥锁与它保护访问的数据段紧密联系在一起:

class somethingShared
{
    int        m_value;
    std::mutex m_mtx;
    public:
    somethingShared( int initialVal ) : m_value( initialVal ) { }
    void increment()
    {
        std::unique_lock<std::mutex> lk(m_mtx);
        ++m_value;
    }
    int value() const { return m_value; }
};
void updateValue( somethingShared & value)
{
    for ( int i = 0; i < 100; ++i )
    {
        value.increment();
    }
}

:注:我没有编译或运行这个,只是为了展示总体思路。