如何用shared_ptr减少锁的争用

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何用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

以上是关于如何用shared_ptr减少锁的争用的主要内容,如果未能解决你的问题,请参考以下文章

SQL 逻辑优化 case when 转为 union all

多线程开发注意问题

如何避免 ConcurrentBag<T>.GetEnumerator() 上的争用

当大多数类并发时如何找出java中的争用问题

使用 Castle 单例是不是会导致 ASP.NET Web 应用程序中的争用

不错MySQL 事务隔离级别