2016-11-19 37 views
1

我在写一个使用C++ 11的多线程程序。我想以原子方式替换一个矢量,而可能有一些其他工作线程遍历旧矢量。我不在乎老员工的工作是否浪费,但我必须确保替代品是原子的,新员工将获得新的工作。我想我可能需要std::atomic<std::shared_ptr<std::vector<T>>>>?但是,由于std::shared_ptr不是可以复制的,所以不能编译。下面的代码(?似乎)的作品,但它泄漏内存:如何以原子方式替换矢量?

#include <atomic> 
#include <memory> 
#include <vector> 
#include <thread> 
#include <cstdio> 

std::atomic<std::vector<int>*> v; 

void read(const char* name) { 
    long sum = 0; 
    for (int x : *v) sum += x; 
    printf("read(%s) sum = %ld\n", name, sum); 
} 

void replace() { 
    v = new std::vector<int>(100, 2); 
} 

int main() { 
    v = new std::vector<int>(10000000, 1); 
    std::thread t1(read, "t1"); 
    std::thread t2(read, "t2"); 
    std::thread t3(replace); 
    t3.join(); 
    std::thread t4(read, "t4"); 
    std::thread t5(read, "t5"); 
    t1.join();t2.join();t4.join();t5.join(); 
} 
+1

您可能想要看看[RCU](https://lwn.net/Articles/262464/)这样的方法,其中编写者可以通过'memory_order_release'自动切换指向结构的指针,而读取器通过'memory_order_consume'安全地读取,并且只有一次认为没有新的读者可以观察旧的指针,作者才能够回收内存。当然,其他记忆回收策略也会起作用,包括危险指针和参考计数。 – Alejandro

+0

t3在t2进入read()函数体之前,这里可以替换向量。 –

回答

1

的shared_ptr已经线程安全的,你没有(也不可能)在atomic<>包裹。只需在您的阅读器中复制shared_ptr,然后在作者填写完新数据后,您可以使用新的。

+0

@ abcdabcd987:只是提示“它有效!”我认为你的测试代码在这个要点上不足以说明问题。我会让他们在自己的循环中使用几百万次'replace'。 –

+2

Umm ...控制块中的引用计数是安全的,但实际的指针交换不保证是原子的;如果一个线程拷贝指针的同时另一个线程正在交换,那么如果硬件无法保证指针写入始终是原子化的,则可能会出现问题。 'shared_ptr'没有'atomic'包装器,但['shared_ptr'有'atomic'函数](http://en.cppreference.com/w/cpp/memory/shared_ptr/atomic).You'd想要做'std :: atomic_store_explicit(&v,std :: make_shared >(100,2),std :: memory_order_release);'和... – ShadowRanger

+1

...在'read'函数中,用'auto localv = std :: atomic_load_explicit(&v,std :: memory_order_acquire);'来获得所需的安全性。 Per cppreference:“如果多个执行线程在没有同步的情况下访问同一个std :: shared_ptr对象,并且任何这些访问都使用shared_ptr的非const成员函数,那么将发生数据竞争,除非通过这些函数执行所有此类访问是相应的原子访问函数的重载(std :: atomic_load,std :: atomic_store等)“ – ShadowRanger