• 如何用shared_ptr减少锁的争用


    在并发环境下锁的使用是家常便饭, 如何减少锁的使用是优化程序性能的一个方面. c++11里面新增了智能指针std::shared_ptr, 这个东西也许能给我们带来些启发. shared_ptr的一个特性是当引用计数为0时,它所拥有的堆内存会被自动释放. 利用这个特性我们可以做点实用的功能, 如下程序:

    #include <assert.h>
    #include <chrono>
    #include <iostream>
    #include <mutex>
    #include <thread>
    
    std::shared_ptr<int> kNumPtr(new int(0));
    std::mutex kmtx;
    
    std::shared_ptr<int> getSharedPtr()
    {
            kmtx.lock();
            std::shared_ptr<int> ptr = kNumPtr;
            kmtx.unlock();
            return ptr;
    }
    
    void dosomething(std::shared_ptr<int> ptr)
    {
            std::cout << "value: " << *ptr << std::endl;
    }
    
    int main()
    {
    
            auto threadProc = [&](){
                            for(size_t i = 0; i < 100; ++i)
                            {
                                    kmtx.lock();
                                    if(!kNumPtr.unique()){
                                            kNumPtr.reset(new int(*kNumPtr));
                                    }
                                    assert(kNumPtr.unique());
                                    *kNumPtr = *kNumPtr + 1;
                                    kmtx.unlock();
                                    std::this_thread::sleep_for(std::chrono::milliseconds(1));
                            }
                    };
    
            std::thread t1(threadProc);
            std::thread t2(threadProc);
            std::thread t3(
                    [&](){
                            for(size_t i = 0; i < 100000; ++i)
                            {
                                    std::shared_ptr<int> ptr = getSharedPtr();
                                    dosomething(ptr);
                            }
                    }
            );
    
            t1.join();
            t2.join();
            t3.join();
    
            std::cout << "kNumPtr's value: " << *kNumPtr << std::endl;
            assert(*kNumPtr = 200);
            assert(kNumPtr.unique());
    }
    

      我们一共启动了三个线程, 这个程序模拟的场景是读的频率远远大于写的频率. 两个写线程模拟对共享数据的修改, 一个读线程用来模拟高频读的行为. dosomething模拟对数据的操作. 通常我们的程序需要在读操作的时候加上锁, 然而这里却只是加了一个锁用来做拷贝智能指针的操作,临界区的长度基本可以忽略, 如果dosomething的耗时很长, 比如服务器网络编程中通常的IO读写, 那么这个锁的开销其实是很大的, 对其它线程共享资源的访问性能延迟很大. 而如果换成现在的代码, 其代价几乎可以忽略.

      再看写线程, 其实写线程用了copy_on_write技术, 将旧有的共享资源拷贝到新的地址空间上, 比较重要的一点是这个旧有的资源其实在reset之后 其引用计数(use_count)的值其实为0, 如果在读线程中处理完毕之后, 操作系统会自动将这份旧有内存进行释放, 这点才是真正有意思的地方.

      程序地址:https://github.com/xiaopeifeng/CodeTricks/blob/master/shared_ptr_copyonwrite.cc

  • 相关阅读:
    简单递归后台代码
    Request.QueryString与Request的区别
    在ASP.NET中实现图片、视频文件上传方式
    git之二
    ORACLE 下查看用户session 及 kill 用户session
    oracle 存储过程、函数和包的权限
    查看Linux操作系统版本命令
    Oracle创建Database link方法
    Oracle修改统计信息收集计划时间
    Oracle 不停库清理监听日志
  • 原文地址:https://www.cnblogs.com/fxplove/p/5222475.html
Copyright © 2020-2023  润新知