shared

it2026-01-05  9

参考:《linux多线程服务器编程---使用module网络库》(陈硕)  第二章 线程同步精要(P53-55)。     

在多线程编程中,如果要用到修改共享资源的地方,如何正确地解决问题并提高效率?

#include"mutex.h"#include<vector>#include<string>MutexLock mutex;classFoo{public:void doit();};std::vector<Foo> foos;void post(constFoo&f){MutexLockGuard lock(&mutex); foos.push_back(f);}void traverse(){MutexLockGuard lock(&mutex); std::vector<Foo>::iterator iter=foos.begin();for(; iter!=foos.end(); iter++){ iter->doit();}}voidFoo::doit(){Foo f; post(f);}int main(){Foo f; post(f); traverse();} 如上面的代码中,由于在traverse()中调用了doit()函数,而doit()中又调用了post,这两个函数里面都有锁存在,如何解决问题?
#include"mutex.h"#include<vector>#include<string>#include<memory>MutexLock mutex;classFoo{public:void doit();};typedef std::vector<Foo>FooList;typedef std::shared_ptr<std::vector<Foo>>FooListPtr;FooListPtr g_foos(new std::vector<Foo>);void post(constFoo&f){MutexLockGuard lock(&mutex);if(!g_foos.unique()){ g_foos.reset(newFooList(*g_foos));} g_foos->push_back(f);}void traverse(){FooListPtr local_foos;{MutexLockGuard lock(&mutex); local_foos =g_foos;} std::vector<Foo>::iterator iter=local_foos->begin();for(; iter!=local_foos->end(); iter++){ iter->doit();}}voidFoo::doit(){Foo f; post(f);}int main(){Foo f; post(f); traverse();} 这里使用了copy_on_write办法来解决这个问题,就是写时拷贝的方法。 第一处:     post()函数中,这里是往vector里面插入新的对象,这有可能破坏迭代器,引起在traverse的时候崩溃。于是它通过判读是否有 其它地方在引用该vector,也就是: if(!g_foos.unique()) 如果有其它地方还在引用,那就把 g_foos重置指向新的地址,旧的地址就在被其它地方使用,当被使用完以后就自动释放,因为引用计数减0了,而且用了reset()。 后面就使用新的 new FooList (* g_foos )。 第二处:     也就是在traverse中, MutexLockGuard lock(&mutex); local_foos =g_foos; 这里使用了一个临时的变量来引用g_foos,这样就可以使得原来被引用的计数加1。标志有其它地方在使用 g_foos。
使用写时拷贝的方法(copy_on_write),就解决了一些必须递归调用锁的问题。这种思想使用得比较广泛,如流表的管理就可以,首先将读写分离,将一个线程里面修改流表,转发线程都只读。
shared_ptr 的引用计数本身是安全且无锁的,但对象的读写则不是. 一个 shared_ptr 对象实体可被多个线程同时读取; 两个 shared_ptr 对象实体可以被两个线程同时写入,“析构”算写操作; 如果要从多个线程读写同一个 shared_ptr 对象,那么需要加锁。          

null

转载于:https://www.cnblogs.com/yml435/p/6929888.html

相关资源:用shared_ptr实现多线程对全局变量的读写,copy-on-write技术
最新回复(0)