目录问题描述测试代码测试结果测试代码介绍别人的经验,我们的阶梯!开发中经常会遇到多个并发执行的线程需要访问同一个资源,即发生资源竞争。在这种场景下,一般的做法是加一把锁,通过锁机制保护临界区,达到独占资源的目的。本文主要介绍使用分段锁来解决这个问题。说起来很简单:就是降低锁的粒度,实现资源独占,最大程度避免竞争。问题描述周末和朋友聊天,聊了聊他们最近的工作。他们有一个项目需要将以前的一个单片机程序移植到x86平台上。由于历史原因,代码中处处充斥着全局变量。要知道:以前的单片机里有很多全局变量,方便好用!在代码中,尽量避免使用全局变量。缺点是:不方便模块化,函数不可重入,耦合度高。..由于大多数单片机只有一个CPU,所以是真正的串行运算。也许你会说:会出现中断,这也是异步操作。没错,但是在访问全局变量的地方可以关闭中断,这样就避免不了资源竞争。但是移植到x86平台后,在多核的情况下,多线程(任务)才是真正的并发执行序列。如果多个线程同时操作某个全局变量,必然存在竞争。针对这个问题,第一个想到的解决方案是:分配一个通用互斥量,不管哪个线程要访问全局变量,先获取锁,然后操作全局变量,操作完再释放锁完成了。但是这种方案有一个很大的问题,就是:当并发线程很多的时候,程序的执行效率太低了。他们最终的解决方案是分段锁,即根据数据索引对全局变量进行分段,并为每一段数据分配一个锁。至于每段的数据长度,需要根据实际业务场景进行调整,以达到最佳性能。回来后觉得这个想法很巧妙。这个机制看似简单,但确实能解决大问题。于是写了一段代码测试一下:这个方案对程序的性能影响有多大。测试代码在测试代码中,定义了一个全局变量:volatileinttest_data[DATA_TOTAL_NUM];数组长度为10000(宏定义:DATA_TOTAL_NUM),然后创建100个线程并发访问这个全局变量,每个线程访问100000次。然后执行3个测试用例:测试1:00个线程在不使用锁的情况下同时操作全局变量,随机生成访问数据的索引,最后统计每个线程的平均执行时间。如果不使用锁,最终的结果(全局变量中的数据内容)一定是错误的。这只是看时间消耗。测试二:使用全局锁(大锁),100个线程使用一把锁。在操作全局变量之前,每个线程在操作全局变量之前必须先获取锁,否则只能阻塞等待其他线程释放锁。测试三:使用分段锁,根据全局变量的长度分配多个锁。每个线程访问时,根据访问数据的索引获取不同的锁,减少了竞争的机会。在这个测试场景中,全局变量test_data的长度为10000,每100条数据分配一个锁,所以一共需要100把锁。例如,在某个时候:线程1想要访问test_data[110]。线程2想要访问test_data[120]。线程3想要访问test_data[9900]。首先根据每个线程要访问的数据索引计算:这个索引对应哪个锁?计算方式:accessindex%计算每个锁对应的数据长度:线程1和线程2将对第二个锁竞争锁;而线程3可以单独获取最后一个锁,这样线程3就避免了与线程1和线程2的竞争。测试结果$./a.outtest1_naked:average=2876mstest2_one_big_lock:average=11233mstest3_segment_lock:average=3216ms来自测试结果,加段锁比使用全局锁好,确实对程序性能提升明显。当然,测试结果与不同的系统环境和业务场景有关,尤其是线程竞争程度、临界区执行时间等等。测试代码简介测试代码不考虑越界情况。例如:一个线程需要访问索引190~210上的数据,而这个区间刚好跨过200的分界点。第0个锁:0~99。第一个锁:100~199。第二个锁:200~299.因此,要访问190~210,需要同时获取第一把和第二把锁。在实际项目中,需要考虑这种越界的情况,只能通过计算起始索引和结束索引来获取这些锁。当然,为了防止死锁,需要按顺序获取。#defineTHREAD_NUMBER100//线程数#defineLOOP_TIMES_EACH_THREAD100000//每个线程执行for循环的次数#defineDATA_TOTAL_NUM10000//全局变量长度#defineSEGMENT_LEN100//多少数据分配一个锁volatileinttest_data[DATA_TOTAL_NUM];//竞争全局变量voidmain(void){test1_naked();test2_one_big_lock();test3_segment_lock();同时(1)睡眠(3);//主线程一直运行,也可以使用getchar();}//测试一:子线程执行的函数void*test1_naked_function(void*arg){structtimevaltm1,tm2;gettimeofday(&tm1,NULL);for(unsignedinti=0;i
