• shared_ptr实现copy_on_write


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

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

    1. #include"mutex.h"
    2. #include<vector>
    3. #include<string>
    4. MutexLock mutex;
    5. classFoo{
    6. public:
    7. void doit();
    8. };
    9. std::vector<Foo> foos;
    10. void post(constFoo&f){
    11. MutexLockGuard lock(&mutex);
    12. foos.push_back(f);
    13. }
    14. void traverse(){
    15. MutexLockGuard lock(&mutex);
    16. std::vector<Foo>::iterator iter=foos.begin();
    17. for(; iter!=foos.end(); iter++){
    18. iter->doit();
    19. }
    20. }
    21. voidFoo::doit(){
    22. Foo f;
    23. post(f);
    24. }
    25. int main(){
    26. Foo f;
    27. post(f);
    28. traverse();
    29. }
    如上面的代码中,由于在traverse()中调用了doit()函数,而doit()中又调用了post,这两个函数里面都有锁存在,如何解决问题?

    1. #include"mutex.h"
    2. #include<vector>
    3. #include<string>
    4. #include<memory>
    5. MutexLock mutex;
    6. classFoo{
    7. public:
    8. void doit();
    9. };
    10. typedef std::vector<Foo>FooList;
    11. typedef std::shared_ptr<std::vector<Foo>>FooListPtr;
    12. FooListPtr g_foos(new std::vector<Foo>);
    13. void post(constFoo&f){
    14. MutexLockGuard lock(&mutex);
    15. if(!g_foos.unique()){
    16. g_foos.reset(newFooList(*g_foos));
    17. }
    18. g_foos->push_back(f);
    19. }
    20. void traverse(){
    21. FooListPtr local_foos;
    22. {
    23. MutexLockGuard lock(&mutex);
    24. local_foos =g_foos;
    25. }
    26. std::vector<Foo>::iterator iter=local_foos->begin();
    27. for(; iter!=local_foos->end(); iter++){
    28. iter->doit();
    29. }
    30. }
    31. voidFoo::doit(){
    32. Foo f;
    33. post(f);
    34. }
    35. int main(){
    36. Foo f;
    37. post(f);
    38. traverse();
    39. }
    这里使用了copy_on_write办法来解决这个问题,就是写时拷贝的方法。
    第一处:
        post()函数中,这里是往vector里面插入新的对象,这有可能破坏迭代器,引起在traverse的时候崩溃。于是它通过判读是否有
    其它地方在引用该vector,也就是:
    1. if(!g_foos.unique())
    如果有其它地方还在引用,那就把g_foos重置指向新的地址,旧的地址就在被其它地方使用,当被使用完以后就自动释放,因为引用计数减0了,而且用了reset()。
    后面就使用新的newFooList(*g_foos)。
    第二处:
        也就是在traverse中,
    1. MutexLockGuard lock(&mutex);
    2. local_foos =g_foos;
    这里使用了一个临时的变量来引用g_foos,这样就可以使得原来被引用的计数加1。标志有其它地方在使用g_foos。

    使用写时拷贝的方法(copy_on_write),就解决了一些必须递归调用锁的问题。这种思想使用得比较广泛,如流表的管理就可以,首先将读写分离,将一个线程里面修改流表,转发线程都只读。

    shared_ptr 的引用计数本身是安全且无锁的,但对象的读写则不是.
    一个 shared_ptr 对象实体可被多个线程同时读取;
    两个 shared_ptr 对象实体可以被两个线程同时写入,“析构”算写操作;
    如果要从多个线程读写同一个 shared_ptr 对象,那么需要加锁。
     
     
     
     
     





  • 相关阅读:
    EF Code First一对一、一对多、多对多关联关系配置
    ASP.NET Core 运行原理剖析2:Startup 和 Middleware(中间件)
    ASP.NET Core 运行原理剖析1:初始化WebApp模版并运行
    ASP.NET Core 1.0 入门——Application Startup¶
    ASP.NET Core管道深度剖析(1):采用管道处理HTTP请求
    EF Code First 学习笔记:约定配置
    ASPNET CORE初探
    EntityFramework 7.0之初探【基于VS 2015】(
    ASP.NET Core 开发
    sqlite3 外键作用原理和使用方法
  • 原文地址:https://www.cnblogs.com/yml435/p/6929888.html
Copyright © 2020-2023  润新知