浅谈多线程编程以及锁的效率测试

来源:互联网 发布:2016淘宝小号直销商 编辑:程序博客网 时间:2024/05/17 03:48

锁在多线程应用上非常广泛,虽然这个影响效率,但这也是在不影响计算结果上最直观的方法了。多线程编程主要有四种思路,一种是加锁,一种是无锁式编程,一种是 STM  软件事务内存,一种是使用 Erlang 等函数式编程语言。

无锁式编程在新手社区中广泛使用,比如我入门时写多线程程序从不加锁大笑,并且可以肯定的是十个程序有九个都有问题。假如有代码高手一改常态使用无锁式编程写代码,可能有两个原因,一是故意留bug,二是工作不饱和。为啥这么说呢?因为,加锁麻烦,无锁式编程的设计更为麻烦,往往消费成倍工作量来换取程序稳定性。所以,朋友们,这个能不用尽量少用。

STM 简要介绍下,就是通过代码实现自动控制内存,假如写内存冲突就会回滚到之前状态。我对这个不了解,不做过多评论。

Erlang 等函数式编程语言的设计就是从分布式以及多线程上面考虑的,非常适合用于项目开发。不过有一个问题,就是函数式编程太难了(网上说的,我觉得还好啊,比编程入门轻松多了),可以试着Bing一下这篇文章《函数式编程很难,这正是你要学习它的原因》。函数式编程的代码不像现在面向对象代码这样清晰直观,也不适用于面向对象编程的程序员快速开发新项目,但它确实是多线程编程首选,没有之一。

还有3D游戏中常常用到的大规模并行计算,也就是调用显卡进行计算,行业规范OpenCL,动不动就是几千上万个线程,虽然在大规模计算面前靠谱,但不适用于几个十几个线程的常规软件,并且在显卡中执行的代码不可以直接访问内存,所以只在少部分项目中有用。

除了上面这些之外其实还有一种,只有大神级程序员才能写出来的代码,就比如单线程的 node.js 。 CPU 计算速度如此之快, I/O 操作如此之慢,为何就不能充分运用计算机的这种特性呢? node.js 应运而生。这个虽然牛逼,但也不是一般程序员能写出来的。使用 node.js 也可以实现这样的效果,但不在此次讨论之列。


总的来说,还是锁来的最实在。毕竟数据的正确性与效率的折衷上,还是正确性重要。

由于操作系统以及硬件配置不同,效率测试结果可能与同学们有所区别,但效率比例应该是差不多的。以下我测试了6钟锁,分别执行两个进程,每个进程分别对内存指定位置进行自增一亿次,通过所需时间来判断锁的效率。

1、intel 指令锁

__asm { lock inc t }

约底层的代码总是效率越高,这个没得说。本机测试结果在 2s 左右浮动。不过由于是内联汇编,可能存在兼容问题。

2、Win32 API 自增锁

InterlockedIncrement(&t);

执行时间在 5s 左右浮动。

3、Win32 临界区锁

CRITICAL_SECTION cs = { 0 };//定义临界区所需要使用的变量InitializeCriticalSection(&cs);//初始化临界区EnterCriticalSection(&cs);//进入临界区LeaveCriticalSection(&cs);//退出临界区DeleteCriticalSection(&cs);//释放临界区
执行时间在 13s 左右浮动。

上面三个为应用层锁,除了这三个外Windows还有内核锁,用于进程间同步。由于内核锁的控制需要在应用层与内核状态间互相切换,导致速度非常慢。在此测试时我将一亿次循环改为了一百万次,执行时间均为 5s 左右,对应上面约为 500s 。

4、Windows 互斥量

HANDLE hMutex = CreateMutex(NULL, FALSE, TEXT("testMutex_"));//创建互斥量WaitForSingleObject(hMutex, INFINITE);//等待指定互斥量结束ReleaseMutex(hMutex);//释放互斥量CloseHandle(hMutex);//关闭互斥量句柄
5、Windows 事件

HANDLE hEvent = CreateEvent(NULL, FALSE, TRUE, TEXT("testEvent_"));//创建事件WaitForSingleObject(hEvent, INFINITE);//等待事件SetEvent(hEvent);//设置事件CloseHandle(hEvent);//关闭句柄
6、Windows 信号量

HANDLE hSema = CreateSemaphore(NULL, 1, 1, TEXT("testSemaphore_"));//创建信号量WaitForSingleObject(hSema, INFINITE);//等待信号ReleaseSemaphore(hSema, 1, NULL);//释放信号量CloseHandle(hSema);//关闭句柄
Windows 信号量主要用于多对象同步互斥,比如5个进程访问3个文件等等。
本来准备试试 boost::thread 和 pthread 的,不过,鉴于跨平台多线程库都有的毛病,暂停都没有,果断不试了。估计效率应该和Windows 临界区差别不大。
各种锁介绍完毕,其中个人感觉在应用层实现的临界区最符合个人思维习惯,在单进程应用中效率也最高,一般小软件最好都使用这种锁。

当然并不是说后面几种锁完全无用,毕竟不同的锁有不同的用武之地,比如跨进程、应用层内核层同步等等,这是应用层锁不能实现的(Windows 的应用层对每个进程分别隔离,进程之间不可互相访问,导致锁不可共用)。

另外,我写了一个小型多线程库,使用临界区,方便Windows下多线程应用开发,具体在http://blog.csdn.net/fawdlstty/article/details/45017217参考。

1 0
原创粉丝点击