cpp:如何在类线程安全的类中访问向量?

问题描述 投票:0回答:1

我昨天在类似的方向上发布了一些内容,但是该问题专门针对互斥量,在引用的“重复”线程中找不到很多答案。我现在想尝试更笼统地询问,我希望可以。

查看此代码:

#include <iostream>
#include <mutex>
#include <vector>
#include <initializer_list>
using namespace std;

class Data {
public:

    void write_data(vector<float>& data) {
        datav = move(data);
    }

    vector<float>* read_data() {
        return(&datav);
    }

    Data(vector<float> in) : datav{ in } {};

private:
    vector<float> datav{};
};

void f1(vector<Data>& in) {
    for (Data& tupel : in) {
        vector<float>& in{ *(tupel.read_data()) };
        for (float& f : in) {
            f += (float)1.0;
        };
    };
}

void f2(vector<Data>& in) {
    for (Data& tupel : in) {
        vector<float>& in{ *(tupel.read_data()) };
        for (float& f : in) {
            cout << f << ",";
        };
    };
}
int main() {
    vector<Data> datastore{};
    datastore.emplace_back(initializer_list<float>{ 0.2, 0.4 });
    datastore.emplace_back(initializer_list<float>{ 0.6, 0.8 });
    vector<float> bigfv(50, 0.3);
    Data demo{ bigfv };
    datastore.push_back(demo);
    thread t1(f1, ref(datastore));
    thread t2(f2, ref(datastore));
    t1.join();
    t2.join();
};

[以我的期望,我猜想我将得到输出值的狂野混合,具体取决于哪个线程首先到达向量值,因此在第三个具有50x0.3f的“演示”向量中,我期望混合0.3(t2首先到达那里)和1.3(t1首先到达那里)作为输出。即使我尝试使用尽可能多的传递引用,直接指针等来避免复制(原始项目使用的数据量很大),代码的行为仍然是定义的(始终为t2,然后为t1访问)。为什么?我不是在两个线程函数中都通过引用直接访问浮点数吗?

您将如何使此向量访问定义明确?我在其他线程中找到的唯一可能的解决方案是:

-将相似大小的unique_ptr数组定义为互斥锁(感觉很糟糕,因为我需要能够将数据容器添加到数据存储区,所以这意味着每次我更改数据存储区的大小时都要清除该数组并重建它?),或

-使向量原子访问(从某种意义上来说,使我的操作如我所愿的是线程安全的,但是向量没有原子不变性,或者在某些非STL-lib中存在吗?)或

-在数据类中写互斥量的包装器?

对于我的项目而言,哪个线程首先访问并不重要,唯一重要的是我可以定义一个线程将整个向量读/写到数据tupel中,而无需另一个线程同时操作数据集。

c++ multithreading thread-safety atomic
1个回答
0
投票

我相信我现在是参考Sam的评论来做到这一点的,而且似乎可行,这是正确的吗?

#include <iostream>
#include <mutex>
#include <vector>
#include <initializer_list>
using namespace std;

class Data {
public:
    unique_ptr<mutex> lockptr{ new mutex };
    void write_data(vector<float>& data) {
        datav = move(data);
    }

    vector<float>* read_data() {
        return(&datav);
    }

    Data(vector<float> in) : datav{ in } {
    };
    Data(const Data&) = delete;
    Data& operator=(const Data&) = delete;
    Data(Data&& old) {
        datav = move(old.datav);
        unique_ptr<mutex> lockptr{ new mutex };
    }
    Data& operator=(Data&& old) {
        datav = move(old.datav);
        unique_ptr<mutex> lockptr{ new mutex };
    }
private:
    vector<float> datav{};
    //mutex lock{};

};

void f1(vector<Data>& in) {
    for (Data& tupel : in) {
        unique_lock<mutex> lock(*(tupel.lockptr));
        vector<float>& in{ *(tupel.read_data()) };
        for (float& f : in) {
            f += (float)1.0;
        };
    };
}

void f2(vector<Data>& in) {
    for (Data& tupel : in) {
        (*(tupel.lockptr)).try_lock();
        vector<float>& in{ *(tupel.read_data()) };
        for (float& f : in) {
            cout << f << ",";
        };
        (*(tupel.lockptr)).unlock();
    };
}
int main() {
    vector<Data> datastore{};
    datastore.emplace_back(initializer_list<float>{ 0.2, 0.4 });
    datastore.emplace_back(initializer_list<float>{ 0.6, 0.8 });
    vector<float> bigfv(50, 0.3);
    Data demo{ bigfv };
    datastore.push_back(move(demo));
    thread t1(f1, ref(datastore));
    thread t2(f2, ref(datastore));
    t1.join();
    t2.join();
};

通过使用unique_ptr,在移动实例时我应该不留内存泄漏,对吗?

© www.soinside.com 2019 - 2024. All rights reserved.