首页 > 其他分享 >锁的实现

锁的实现

时间:2024-03-11 12:44:30浏览次数:25  
标签:实现 lock counter futex threads vec test

锁的实现

互斥锁

锁的开销

机制

摘录自:https://www.cnblogs.com/MrLiuZF/p/15143976.html

现在锁的机制一般使用futex(fast userspace mutexes),即内核态和用户态的混合机制。

在futex之前,内核维护一个对象,这个对象对所有进程可见,这个对象用来管理互斥锁并且通知阻塞的进程。如果进程A要进入临界区,先去内核查看这个对象,有没有别的进程在占用这个临界区,出临界区的时候,也去内核查看这个对象,有没有别的进程在等待进入临界区,然后根据一定的策略唤醒等待的进程。这些不必要的系统调用造成了大量的性能开销。

futex是一种用户态和内核态混合的同步机制。首先,同步的进程间通过mmap共享一段内存,futex变量就位于这段共享的内存中且操作是原子的。当进程尝试进入或者退出互斥区的时候,先去查看共享内存中的futex变量,如果没有竞争发生,则只修改futex,而不用再执行系统调用了。当通过访问futex变量告诉进程有竞争发生,则还是得执行系统调用去完成相应的处理(wait或者wake up)。简单的说,futex就是通过在用户态的检查,如果没有竞争就不需要陷入内核,大大提高了low-contention时的效率。

mutex是在futex的基础上用内存共享变量实现的。如果共享变量建立在进程内,它就是一个线程锁,如果建立在进程间共享内存上,它就是一个进程锁。pthread_mutex_t中的_lock字段用户标记占用情况,先使用CAS判断_lock是否占用,若未占用,则直接返回。否则通过__lll_lock_wait_private调用SYS_futex系统调用迫使线程进入沉睡。CAS是用户态的CPU指令,若无竞争,简单修改锁状态即返回,非常高效。只有发现竞争,才通过系统调用陷入内核态。

所以如果锁不存在冲突,每次获得锁和释放锁的开销仅仅是CAS指令的开销。

竞争条件和非竞争条件下的锁开销试验



#include <chrono>
#include <stdio.h>
#include <mutex>
#include <thread>
#include <vector>
#include <memory>

int counter = 0;
std::mutex g_mutex;

/* Get current time in nano-seconds. */
auto lambda_now = []()
{
    using namespace std::chrono;
    auto now_count = duration_cast<nanoseconds>(system_clock::now().time_since_epoch()).count();
    return now_count;
};

/* Add 1 to @counter for certain times, calculate
 how much time spent, store the time to @a. */
void trd_work(int &a)
{   
    const int times = 1 * 1000 * 10;
    auto t1 = lambda_now();
    for (int i = 0; i < times; ++i)
    {
        g_mutex.lock();
        counter += 1;
        g_mutex.unlock();
    }
    auto t2 = lambda_now();

    a = (int)(t2-t1);
}

int main(int argc, char **argv)
{
    if (argc != 2)
    {
        printf("Usage: %s <thread num>\n", argv[0]);
        return EXIT_FAILURE;
    }

    std::size_t num_threads = atoi(argv[1]);

    std::vector<int> vec_sum;
    std::vector<std::shared_ptr<std::thread>> vec_threads;
    vec_sum.resize(num_threads);
    vec_threads.resize(num_threads);

    for (std::size_t i = 0; i < num_threads; ++i)
    {
        vec_threads[i] = std::make_shared<std::thread>(trd_work, std::ref(vec_sum[i]));
    }

    for (auto i : vec_threads)
    {
        i->join();
    }

    int total_time = 0;
    for (auto i : vec_sum)
    {
        total_time += i;
    }

    printf("%d ns/lock, counter=%d\n", total_time / counter, counter);

    return 0;
}

test@test:~/tmp$ g++ test.cpp -O3
test@test:~/tmp$ ./a.out 1
31 ns/lock, counter=10000
test@test:~/tmp$ ./a.out 2
155 ns/lock, counter=20000
test@test:~/tmp$ ./a.out 4
343 ns/lock, counter=40000
test@test:~/tmp$ ./a.out 8
748 ns/lock, counter=80000

上述测试代码中,各个线程通过加锁的方式,对全局变量进行特定次数的+1操作。先不考虑互斥锁的耗时,此时线程越多,需要等待的时间越长(因为同一时间只能有一个线程操作),并且等待的时间和线程数成正比。

但是,从试验结果看,仅在线程数大于2之后,这个比例关系才成立。因此,有理由相信,有竞争关系时相比没有竞争关系时,锁操作会消耗更多的时间。由试验数据估算,单个线程的耗时应为75ns左右,实际为30ns左右,因此耗时近一倍。

libc --> NPTL(pthread)

标签:实现,lock,counter,futex,threads,vec,test
From: https://www.cnblogs.com/amazzzzzing/p/18065824

相关文章

  • WPF 实现文件/文件夹监听工具
    参考gpt环境软件/系统版本说明WindowsWindows10专业版22H219045.4046MicrosoftVisualStudioMicrosoftVisualStudioCommunity2022(64位)-17.6.5Microsoft.NetSDK8.0.101手动安装Microsoft.NetSDK7.0.306MicrosoftVisualStudio......
  • MySQL实现事务隔离的原理
    一、readview四个字段create_trx_id:创建该readview的事务的事务idm_ids:创建readview时,当前数据库中的活跃事务(指启动但还没提交的事务)min_trx_id:m_ids的最小值max_trx_id:创建readview后,下一个事务的id二、聚簇索引的隐藏列trx_id:最近一次改动该聚簇索引记录的事务idrol......
  • WIFI&蓝牙(ESP32)转CAN总线&串口TTL模块-C1-设备作为Modbus Slave实现RS485 Modbus RT
    <p><iframename="ifd"src="https://mnifdv.cn/resource/cnblogs/ESP32_CAN"frameborder="0"scrolling="auto"width="100%"height="1500"></iframe></p> 说明这节测试的是让设备作为Modbus......
  • WPF实现颜色选择器
    先看效果图; 再说一下思路: 打开设计器,属性里面找到"颜色",设置为渐变色,将渐变色设置为9段,分别是,红橙黄绿青蓝紫白黑(Red,Orange,Yellow,Lime,Cyan,Blue,Magenta,White,Black);然后移动滑块儿,比如在红色和橙色和黄色之间移动的时候,会发现颜色的RGB值是有规律的变化的,R是固定......
  • vue项目实现PC端各分辨率适配
    最近做项目刚好用到pc端分辨率适配,在这里分享一下我的做法。1.先下载需要的插件包px2rem-loader、postcss-pxtoremnpminstallpx2rem-loadernpminstallpostcss-pxtorem@5.1.12.配置rem.js文件(名称自己随意取就可以),一般放置在utils文件夹里,没有就新建一个utils文件......
  • 使用@Autowired + Map 实现策略模式
    创建接口publicinterfaceUserService{StringgetName();}创建多个类实现上面的接口实现一importcom.boot.service.UserService;importorg.springframework.stereotype.Service;@Service("zhangsan")publicclassZhangsanUserServiceImplimplementsUserServ......
  • js 实现深拷贝/深复制
    //深拷贝constdeepClone=(obj)=>{vartarget={};for(varkeyinobj){if(Object.prototype.hasOwnProperty.call(obj,key)){if(typeofobj[key]==='object'){target[key]=deepClone(obj[key])......
  • 政府军工等行业物理隔离后 如何实现文件安全导入导出?
    政府、军工等重点行业的核心数据比较多,比如国家机密、军事情报、人员信息、技术数据、财务数据等,一旦泄露将会造成不可挽回的影响。所以政府、军工这些行业需要使用一些手段将数据保护起来,其中网络隔离就是一个比较普遍的方式,隔离后,再使用一些文件安全导入导出的工具或产品,来进行......
  • 并行化优化KD树算法:使用C#实现高效的最近邻搜索
    本文信息中文名:《并行化优化KD树算法:使用C#实现高效的最近邻搜索》英文名:"ParallelizedOptimizationofKD-TreeAlgorithm:ImplementingEfficientNearestNeighborSearchinC#"摘要本文介绍了如何使用并行计算技术优化KD树算法,并使用C#编程语言实现了高效的最近邻......
  • 接口的详解 :接口 对象=new 实现类
    首先接口是一个特殊的抽象类既然是类就会创建对象 接口是为了实现多态接口是为了实现多态。接口是为了实现多态。接口回调:interfacePeople{voidpeopleList();}classStudentimplementsPeople{publicvoidpeopleList(){System.out.println("I’m......