【转】互斥对象锁和临界区锁性能比较

原作者:chexlong 原文地址:http://blog.csdn.net/chexlong/article/details/7060425

在Win32平台上进行多线程编程,常会用到锁。下边用C++实现了互斥对象(Mutex)锁和临界区(CRITICAL_SECTION)锁,以加深理解和今后方便使用。代码已在VS2005环境下编译测试通过。

Lock.h

[cpp] view plaincopy

  1. #ifndef _Lock_H
  2. #define _Lock_H
  3. #include <windows.h>
  4. //锁接口类
  5. class ILock
  6. {
  7. public:
  8. virtual ~ILock() {}
  9. virtual void Lock() const = 0;
  10. virtual void Unlock() const = 0;
  11. };
  12. //互斥对象锁类
  13. class Mutex : public ILock
  14. {
  15. public:
  16. Mutex();
  17. ~Mutex();
  18. virtual void Lock() const;
  19. virtual void Unlock() const;
  20. private:
  21. HANDLE m_mutex;
  22. };
  23. //临界区锁类
  24. class CriSection : public ILock
  25. {
  26. public:
  27. CriSection();
  28. ~CriSection();
  29. virtual void Lock() const;
  30. virtual void Unlock() const;
  31. private:
  32. CRITICAL_SECTION m_critclSection;
  33. };
  34. //锁
  35. class CMyLock
  36. {
  37. public:
  38. CMyLock(const ILock&);
  39. ~CMyLock();
  40. private:
  41. const ILock& m_lock;
  42. };
  43. #endif

Lock.cpp

[cpp] view plaincopy

  1. #include "Lock.h"
  2. //---------------------------------------------------------------------------
  3. //创建一个匿名互斥对象
  4. Mutex::Mutex()
  5. {
  6. m_mutex = ::CreateMutex(NULL, FALSE, NULL);
  7. }
  8. //销毁互斥对象,释放资源
  9. Mutex::~Mutex()
  10. {
  11. ::CloseHandle(m_mutex);
  12. }
  13. //确保拥有互斥对象的线程对被保护资源的独自访问
  14. void Mutex::Lock() const
  15. {
  16. DWORD d = WaitForSingleObject(m_mutex, INFINITE);
  17. }
  18. //释放当前线程拥有的互斥对象,以使其它线程可以拥有互斥对象,对被保护资源进行访问
  19. void Mutex::Unlock() const
  20. {
  21. ::ReleaseMutex(m_mutex);
  22. }
  23. //---------------------------------------------------------------------------
  24. //初始化临界资源对象
  25. CriSection::CriSection()
  26. {
  27. ::InitializeCriticalSection(&m_critclSection);
  28. }
  29. //释放临界资源对象
  30. CriSection::~CriSection()
  31. {
  32. ::DeleteCriticalSection(&m_critclSection);
  33. }
  34. //进入临界区,加锁
  35. void CriSection::Lock() const
  36. {
  37. ::EnterCriticalSection((LPCRITICAL_SECTION)&m_critclSection);
  38. }
  39. //离开临界区,解锁
  40. void CriSection::Unlock() const
  41. {
  42. ::LeaveCriticalSection((LPCRITICAL_SECTION)&m_critclSection);
  43. }
  44. //---------------------------------------------------------------------------
  45. //利用C++特性,进行自动加锁
  46. CMyLock::CMyLock(const ILock& m) : m_lock(m)
  47. {
  48. m_lock.Lock();
  49. }
  50. //利用C++特性,进行自动解锁
  51. CMyLock::~CMyLock()
  52. {
  53. m_lock.Unlock();
  54. }

下边是测试代码

[cpp] view plaincopy

  1. // MyLock.cpp : 定义控制台应用程序的入口点。
  2. //
  3. #include <iostream>
  4. #include <process.h>
  5. #include <time.h>
  6. #include "Lock.h"
  7. using namespace std;
  8. #define ENABLE_MUTEX
  9. #define ENABLE_CRITICAL_SECTION
  10. #if defined (ENABLE_MUTEX)
  11. //创建一个互斥对象类型锁
  12. Mutex g_Lock;
  13. #elif defined (ENABLE_CRITICAL_SECTION)
  14. //创建一个临界区类型锁
  15. CriSection g_Lock;
  16. #endif
  17. void LockCompare(int &iNum)
  18. {
  19. CMyLock lock1(g_Lock);
  20. iNum++;
  21. }
  22. //线程函数
  23. unsigned int __stdcall StartThread(void *pParam)
  24. {
  25. char *pMsg = (char *)pParam;
  26. if (!pMsg)
  27. {
  28. return (unsigned int)1;
  29. }
  30. CMyLock lock2(g_Lock);
  31. clock_t tStart,tEnd;
  32. tStart = clock();
  33. int iNum = 0;
  34. for (int i = 0; i < 100000; i++)
  35. {
  36. LockCompare(iNum);
  37. }
  38. tEnd = clock();
  39. #if defined (ENABLE_MUTEX)
  40. cout<<"The lock type is mutex, time = "<<(tEnd - tStart)<<" ms."<<endl;
  41. #elif defined (ENABLE_CRITICAL_SECTION)
  42. cout<<"The lock type is critical section, time = "<<(tEnd - tStart)<<" ms."<<endl;
  43. #endif
  44. return (unsigned int)0;
  45. }
  46. int main(int argc, char* argv[])
  47. {
  48. HANDLE hThread1, hThread2;
  49. unsigned int uiThreadId1, uiThreadId2;
  50. char *pMsg1 = "First print thread.";
  51. char *pMsg2 = "Second print thread.";
  52. //创建两个工作线程,分别打印不同的消息
  53. hThread1 = (HANDLE)_beginthreadex(NULL, 0, &StartThread, (void *)pMsg1, 0, &uiThreadId1);
  54. hThread2 = (HANDLE)_beginthreadex(NULL, 0, &StartThread, (void *)pMsg2, 0, &uiThreadId2);
  55. //等待线程结束
  56. DWORD dwRet = WaitForSingleObject(hThread1,INFINITE);
  57. if ( dwRet == WAIT_TIMEOUT )
  58. {
  59. TerminateThread(hThread1,0);
  60. }
  61. dwRet = WaitForSingleObject(hThread2,INFINITE);
  62. if ( dwRet == WAIT_TIMEOUT )
  63. {
  64. TerminateThread(hThread2,0);
  65. }
  66. //关闭线程句柄,释放资源
  67. ::CloseHandle(hThread1);
  68. ::CloseHandle(hThread2);
  69. system("pause");
  70. return 0;
  71. }

在线程函数StartThread中,循环100000次,对保护资源“iNum ”反复加锁,解锁。编译,运行5次,将每次打印的线程锁切换耗时时间记录下来。之后,将测试代码中的宏 #define ENABLE_MUTEX 注释掉,禁掉互斥锁,启用临界区锁,重新编译代码,运行5次。下边是分别是互斥锁和临界区锁耗时记录(不同机器上耗时会不同):

互斥锁


线程Id


耗时 / ms


总计


1


141


125


125


125


125


641


2


140


125


140


125


156


686

临界区锁


线程Id


耗时 / ms


总计


1


15


16


31


31


31


124


2


31


31


31


16


31


140

互斥锁总共耗时:641+686=1327 ms,而临界区锁:124+140=264 ms。显而易见,临界区锁耗时比互斥锁耗时节约了大概5倍的时间。

总结:1、在同一个进程的多线程同步锁,宜用临界区锁,它比较节约线程上下文切换带来的系统开销。但因临界区工作在用户模式下,所以不能对不同进程中的多线程进行同步。2、因互斥对象锁属于内核对象,所以在进行多线程同步时速度会比较慢,但是可以在不同进程的多个线程之间进行同步。

时间: 2024-08-26 04:48:18

【转】互斥对象锁和临界区锁性能比较的相关文章

java对象锁和类锁

参考 http://www.cnblogs.com/yyyyy5101/archive/2011/07/20/2112157.html http://www.cnblogs.com/kkcheng/archive/2011/02/25/1964521.html http://my.oschina.net/billowworld/blog/120766 1.java对象锁 所有对象都自动含有单一的锁.JVM负责跟踪对象被加锁的次数.如果一个对象被解锁,其计数变为0.在任务(线程)第一次给对象加锁的

互斥锁与自旋锁

1.互斥锁原理 在编程中,引入了对象互斥锁的概念,来保证共享数据操作的完整性.每个对象都对应于一个可称为" 互斥锁" 的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象. 互斥锁,是一种信号量,常用来防止两个进程或线程在同一时刻访问相同的共享资源.可以保证以下三点: (1)原子性:把一个互斥量锁定为一个原子操作,这意味着操作系统(或pthread函数库)保证了如果一个线程锁定了一个互斥量,没有其他线程在同 一时间可以成功锁定这个互斥量. (2)唯一性:如果一个线程锁定了一个互

java 对象锁和类锁的区别(转)

java 对象锁和类锁的区别   转自 <http://zhh9106.iteye.com/blog/2151791> 在java编程中,经常需要用到同步,而用得最多的也许是synchronized关键字了,下面看看这个关键字的用法. 因为synchronized关键字涉及到锁的概念,所以先来了解一些相关的锁知识. java的内置锁:每个java对象都可以用做一个实现同步的锁,这些锁成为内置锁.线程进入同步代码块或方法的时候会自动获得该锁,在退出同步代码块或方法时会释放该锁.获得内置锁的唯一途

zbb20180929 thread 自旋锁、阻塞锁、可重入锁、悲观锁、乐观锁、读写锁、对象锁和类锁

1.自旋锁自旋锁可以使线程在没有取得锁的时候,不被挂起,而转去执行一个空循环,(即所谓的自旋,就是自己执行空循环),若在若干个空循环后,线程如果可以获得锁,则继续执行.若线程依然不能获得锁,才会被挂起.使用自旋锁后,线程被挂起的几率相对减少,线程执行的连贯性相对加强.因此,对于那些锁竞争不是很激烈,锁占用时间很短的并发线程,具有一定的积极意义,但对于锁竞争激烈,单线程锁占用很长时间的并发程序,自旋锁在自旋等待后,往往毅然无法获得对应的锁,不仅仅白白浪费了CPU时间,最终还是免不了被挂起的操作 ,

Java 中15种锁的介绍:公平锁,可重入锁,独享锁,互斥锁,乐观锁,分段锁,自旋锁等等(转)

Java 中15种锁的介绍 在读很多并发文章中,会提及各种各样锁如公平锁,乐观锁等等,这篇文章介绍各种锁的分类.介绍的内容如下: 公平锁 / 非公平锁 可重入锁 / 不可重入锁 独享锁 / 共享锁 互斥锁 / 读写锁 乐观锁 / 悲观锁 分段锁 偏向锁 / 轻量级锁 / 重量级锁 自旋锁 上面是很多锁的名词,这些分类并不是全是指锁的状态,有的指锁的特性,有的指锁的设计,下面总结的内容是对每个锁的名词进行一定的解释. 公平锁 / 非公平锁 公平锁 公平锁是指多个线程按照申请锁的顺序来获取锁. 非公

python37 1.GIL--全局解释器锁 2.GIL带来的问题 3.为什么需要GIL 4.GIL的加锁解锁时机 5.关于GIL的性能的讨论 6.线程常用方法 7.GIL锁与自定义锁的区别 8.进程池与线程池 9.同步异步 10.异步调用

复习1.JoinableQueue--可以被join的队列2.多线程3线程的使用方法与进程一模一样3.1守护线程3.2线程安全问题3.3解决方案3.3.1互斥锁mutex3.3.2递归锁Rlock3.3.3信号量semaphore3.3.4死锁问题 详解:1.JoinableQueue--可以被join的队列 1.1join是等待任务结束 队列怎么叫结束 调用task_done一次则表示有一个数据被处理完成了,当task_done次数等于put的次数就意味着任务处理完成了 1.2这就是join的

C# lock 语法糖实现原理--《.NET Core 底层入门》之自旋锁,互斥锁,混合锁,读写锁

原文:C# lock 语法糖实现原理--<.NET Core 底层入门>之自旋锁,互斥锁,混合锁,读写锁 在多线程环境中,多个线程可能会同时访问同一个资源,为了避免访问发生冲突,可以根据访问的复杂程度采取不同的措施 原子操作适用于简单的单个操作,无锁算法适用于相对简单的一连串操作,而线程锁适用于复杂的一连串操作 原子操作 修改状态要么成功且状态改变,要么失败且状态不变,并且外部只能观察到修改前或者修改后的状态,修改中途的状态不能被观察到 .NET 中,System.Threading.Inte

高效编程之互斥锁和自旋锁的一些知识

两种锁的加锁原理 互斥锁:线程会从sleep(加锁)-->running(解锁),过程中有上下文的切换,cpu的抢占,信号的发送等开销. 自旋锁:线程一直是running(加锁-->解锁),死循环检测锁的标志位,机制不复杂. 两种锁的区别 互斥锁的起始原始开销要高于自旋锁,但是基本是一劳永逸,临界区持锁时间的大小并不会对互斥锁的开销造成影响,而自旋锁是死循环检测,加锁全程消耗cpu,起始开销虽然低于互斥锁,但是随着持锁时间,加锁的开销是线性增长. 两种锁的应用 互斥锁用于临界区持锁时间比较长的

python并发编程之多进程(二):互斥锁(同步锁)&amp;进程其他属性&amp;进程间通信(queue)&amp;生产者消费者模型

一,互斥锁,同步锁 进程之间数据不共享,但是共享同一套文件系统,所以访问同一个文件,或同一个打印终端,是没有问题的, 竞争带来的结果就是错乱,如何控制,就是加锁处理 part1:多个进程共享同一打印终端 #并发运行,效率高,但竞争同一打印终端,带来了打印错乱 from multiprocessing import Process import os,time def work(): print('%s is running' %os.getpid()) time.sleep(2) print('