CCriticalSection的使用

CCriticalSection的使用CCriticalSection是对关键段CRITICAL_SECTION的封装。 关键段(critivalsection)是一小段代码,他在执行之前需要独占对一些共享资源的访问权。这种方式可以让多行代码以“原子方式”来对资源进行操控。这里的“原子方式”,指的是代码知道除了当前线程之外没有其他任何线程会同时访问该资源。当然,系统仍然可以暂停当前线程去调度其他线程。但是,在当前线程离开关键

大家好,又见面了,我是你们的朋友全栈君。CCriticalSection是对关键段CRITICAL_SECTION的封装。

 

关键段(critival section)是一小段代码,他在执行之前需要独占对一些共享资源的访问权。这种方式可以让多行代码以“原子方式”来对资源进行操控。这里的“原子方式”,指的是代码知道除了当前线程之外没有其他任何线程会同时访问该资源。当然,系统仍然可以暂停当前线程去调度其他线程。但是,在当前线程离开关键段之前,系统是不会去调度任何想要访问同一资源的其他线程的。

例如:如果两个线程同时访问一个链表,一个线程可能会在另一个线程搜寻元素的同时向链表中添加一个元素,将导致搜索结果不正确;还有可能两个线程同时向链表中添加元素,这种情况会变的更加混乱;甚至一个线程搜索的时候,另一个线程删除了链表节点,将直接导致程序崩溃。

解决这个问题,我们可以先在代码中定义一个CRITICAL_SECTION数据结构m_sect,然后把任何需要访问共享资源的代码放在EnterCriticalSection和LeaveCriticalSection之间。

 

[cpp] view plain copy print?在CODE上查看代码片派生到我的代码片

EnterCriticalSection(&m_sect);  

// 共享资源的代码段….  

  

LeaveCriticalSection(&m_sect);  

一个 CRITICAL_SECTION结构就像是飞机上的一个卫生间,而马桶则是我们想要保护的资源(用EnterCriticalSection和LeaveCriticalSection组成的围墙包围住“马桶”)。由于卫生间很小,因此在同一时刻只允许一个人在卫生间内使用马桶(在同一时刻只允许一个线程在关键段中使用被保护资源)。

如果有多个总是应该在一起使用的资源,那么我们可以把他们放在同一个“卫生间”中:只需要创建一个CRITICAL_SECTION结构来保护所有这些资源。

 

关于关键段,需要掌握以下几点:

1、任何要访问共享资源的代码,都必须包含在EnterCriticalSection和LeaveCriticalSection之间。如果忘了哪怕是一个地方,共享资源就有可能被破坏。忘记调用EnterCriticalSection和LeaveCriticalSection,就好像是未经许可就强制进入卫生间一样,线程强行进入并对资源进行操控。只要有一个线程有这种粗暴的行为,资源就会被破坏。

2、关键段CRITICAL_SECTION是个未公开的结构,因为Microsoft认为开发人员不需要理解这个结构的细节。对我们来说,不需要知道这个结构中的成员变量,我们绝对不应该在编写代码的时候用到他的成员。

3、为了对CRITICAL_SECTION结构进行操控,我们必须调用Windows API函数,传入结构的地址。(注意是地址!)也就是说,如果该CRITICAL_SECTION结构生命周期没有结束,那么可以将该结构地址通过自己喜欢的任何方式传给任何线程。

4、在任何线程试图访问被保护的资源之前,必须对CRITICAL_SECTION结构的内部成员进程初始化。我们不知道内部成员,但可以调用Windows函数实现:VOID WINAPI InitializeCriticalSection(__out LPCRITICAL_SECTION lpCriticalSection);

5、当线程不再需要访问共享资源的时候,应调用下面的函数来清理该结构:VOID WINAPI DeleteCriticalSection(__inout LPCRITICAL_SECTION lpCriticalSection);

 

6、其实CRITICAL_SECTION并不知道什么是共享资源,也不会智能保护共享资源。其根本是,同一个时刻如果有多个线程调用EnterCriticalSection的时候,只有一个线程返回,其余线程则暂停执行,等待前面线程调用LeaveCriticalSection之后再执行。

 

7、可以看出,进入关键段是没有超时设定的,好像永远不会超时。实际上,对EnterCriticalSection的调用也会超时并引发异常。超时的时间长度由下面这个注册表子项中包含的CriticalSectionTimeout值决定:

HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager

这个值以秒为单位,他的默认值为2592000秒,大约30天。

 

8、同一个线程可以随便进入用一个关键段N次,也就是说同一个线程调用EnterCriticalSection无论几次都会返回。不同线程是否能够进入关键段,要看EnterCriticalSection的参数(CRITICAL_SECTION结构的地址)之前是否有线程进入过。要记住:飞机上的卫生间有多个,你可以随便进入无人的卫生间,不能进入有人的卫生间。

 

 

弄明白了CRITICAL_SECTION之后,使用CCriticalSection非常方便,如虎添翼。看代码:

//头文件

[cpp] view plain copy print?在CODE上查看代码片派生到我的代码片

class CCriticalSection : public CSyncObjet  

{…  

public:  

    CRITICAL_SECTION m_sect;  

public:  

    BOOL Unlock();  

    BOOL Lock();  

    BOOL Lock(DWORD dwTimeout);  

…  

}  

 // 构造函数

[cpp] view plain copy print?在CODE上查看代码片派生到我的代码片

CCriticalSection::CCriticalSection() : CSyncObject(NULL)  

{         

    HRESULT hr = S_OK;  

    if (!InitializeCriticalSectionAndSpinCount(&m_sect, 0))//可以理解为InitializeCriticalSection,为了效率,加了一个旋转锁。  

    {  

        hr =  HRESULT_FROM_WIN32(GetLastError());  

    }  

      

    if (FAILED(hr))  

    {  

        AtlThrow(hr);  

    }         

}  

 

//进入关键段

[cpp] view plain copy print?在CODE上查看代码片派生到我的代码片

BOOL CCriticalSection::Lock()  

{     

    ::EnterCriticalSection(&m_sect);   

  

    return TRUE;   

}  

 // 离开关键段

[cpp] view plain copy print?在CODE上查看代码片派生到我的代码片

BOOL CCriticalSection::Unlock()  

{   

    ::LeaveCriticalSection(&m_sect);   

      

    return TRUE;   

}  

 // 析构

[cpp] view plain copy print?在CODE上查看代码片派生到我的代码片

CCriticalSection::~CCriticalSection()  

{   

    ::DeleteCriticalSection(&m_sect);   

}  是对关键段CRITICAL_SECTION的封装。

 

关键段(critival section)是一小段代码,他在执行之前需要独占对一些共享资源的访问权。这种方式可以让多行代码以“原子方式”来对资源进行操控。这里的“原子方式”,指的是代码知道除了当前线程之外没有其他任何线程会同时访问该资源。当然,系统仍然可以暂停当前线程去调度其他线程。但是,在当前线程离开关键段之前,系统是不会去调度任何想要访问同一资源的其他线程的。

例如:如果两个线程同时访问一个链表,一个线程可能会在另一个线程搜寻元素的同时向链表中添加一个元素,将导致搜索结果不正确;还有可能两个线程同时向链表中添加元素,这种情况会变的更加混乱;甚至一个线程搜索的时候,另一个线程删除了链表节点,将直接导致程序崩溃。

解决这个问题,我们可以先在代码中定义一个CRITICAL_SECTION数据结构m_sect,然后把任何需要访问共享资源的代码放在EnterCriticalSection和LeaveCriticalSection之间。

 

[cpp] view plain copy print?在CODE上查看代码片派生到我的代码片

EnterCriticalSection(&m_sect);  

// 共享资源的代码段….  

  

LeaveCriticalSection(&m_sect);  

一个 CRITICAL_SECTION结构就像是飞机上的一个卫生间,而马桶则是我们想要保护的资源(用EnterCriticalSection和LeaveCriticalSection组成的围墙包围住“马桶”)。由于卫生间很小,因此在同一时刻只允许一个人在卫生间内使用马桶(在同一时刻只允许一个线程在关键段中使用被保护资源)。

如果有多个总是应该在一起使用的资源,那么我们可以把他们放在同一个“卫生间”中:只需要创建一个CRITICAL_SECTION结构来保护所有这些资源。

 

关于关键段,需要掌握以下几点:

1、任何要访问共享资源的代码,都必须包含在EnterCriticalSection和LeaveCriticalSection之间。如果忘了哪怕是一个地方,共享资源就有可能被破坏。忘记调用EnterCriticalSection和LeaveCriticalSection,就好像是未经许可就强制进入卫生间一样,线程强行进入并对资源进行操控。只要有一个线程有这种粗暴的行为,资源就会被破坏。

2、关键段CRITICAL_SECTION是个未公开的结构,因为Microsoft认为开发人员不需要理解这个结构的细节。对我们来说,不需要知道这个结构中的成员变量,我们绝对不应该在编写代码的时候用到他的成员。

3、为了对CRITICAL_SECTION结构进行操控,我们必须调用Windows API函数,传入结构的地址。(注意是地址!)也就是说,如果该CRITICAL_SECTION结构生命周期没有结束,那么可以将该结构地址通过自己喜欢的任何方式传给任何线程。

4、在任何线程试图访问被保护的资源之前,必须对CRITICAL_SECTION结构的内部成员进程初始化。我们不知道内部成员,但可以调用Windows函数实现:VOID WINAPI InitializeCriticalSection(__out LPCRITICAL_SECTION lpCriticalSection);

5、当线程不再需要访问共享资源的时候,应调用下面的函数来清理该结构:VOID WINAPI DeleteCriticalSection(__inout LPCRITICAL_SECTION lpCriticalSection);

 

6、其实CRITICAL_SECTION并不知道什么是共享资源,也不会智能保护共享资源。其根本是,同一个时刻如果有多个线程调用EnterCriticalSection的时候,只有一个线程返回,其余线程则暂停执行,等待前面线程调用LeaveCriticalSection之后再执行。

 

7、可以看出,进入关键段是没有超时设定的,好像永远不会超时。实际上,对EnterCriticalSection的调用也会超时并引发异常。超时的时间长度由下面这个注册表子项中包含的CriticalSectionTimeout值决定:

HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager

这个值以秒为单位,他的默认值为2592000秒,大约30天。

 

8、同一个线程可以随便进入用一个关键段N次,也就是说同一个线程调用EnterCriticalSection无论几次都会返回。不同线程是否能够进入关键段,要看EnterCriticalSection的参数(CRITICAL_SECTION结构的地址)之前是否有线程进入过。要记住:飞机上的卫生间有多个,你可以随便进入无人的卫生间,不能进入有人的卫生间。

 

 

弄明白了CRITICAL_SECTION之后,使用CCriticalSection非常方便,如虎添翼。看代码:

//头文件

[cpp] view plain copy print?在CODE上查看代码片派生到我的代码片

class CCriticalSection : public CSyncObjet  

{…  

public:  

    CRITICAL_SECTION m_sect;  

public:  

    BOOL Unlock();  

    BOOL Lock();  

    BOOL Lock(DWORD dwTimeout);  

…  

}  

 // 构造函数

[cpp] view plain copy print?在CODE上查看代码片派生到我的代码片

CCriticalSection::CCriticalSection() : CSyncObject(NULL)  

{         

    HRESULT hr = S_OK;  

    if (!InitializeCriticalSectionAndSpinCount(&m_sect, 0))//可以理解为InitializeCriticalSection,为了效率,加了一个旋转锁。  

    {  

        hr =  HRESULT_FROM_WIN32(GetLastError());  

    }  

      

    if (FAILED(hr))  

    {  

        AtlThrow(hr);  

    }         

}  

 

//进入关键段

[cpp] view plain copy print?在CODE上查看代码片派生到我的代码片

BOOL CCriticalSection::Lock()  

{     

    ::EnterCriticalSection(&m_sect);   

  

    return TRUE;   

}  

 // 离开关键段

[cpp] view plain copy print?在CODE上查看代码片派生到我的代码片

BOOL CCriticalSection::Unlock()  

{   

    ::LeaveCriticalSection(&m_sect);   

      

    return TRUE;   

}  

 // 析构

[cpp] view plain copy print?在CODE上查看代码片派生到我的代码片

CCriticalSection::~CCriticalSection()  

{   

    ::DeleteCriticalSection(&m_sect);   

}  

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/162025.html原文链接:https://javaforall.net

(0)
上一篇 2022年7月20日 下午10:00
下一篇 2022年7月20日 下午10:16


相关推荐

  • Midjourney中文提示词入门指南

    Midjourney中文提示词入门指南

    2026年3月15日
    2
  • sudo命令 sudoers文件

    sudo命令 sudoers文件超级用户权限授权规则含义root ALL=(ALL:ALL)ALL用户名root ALL=(ALL:ALL)ALL主机名root ALL=(**ALL:ALL**)ALL目标用户:组,以谁的身份去执行root ALL=(ALL:ALL)**ALL**命令别名定义>Host_Alias:主机的列表>User_Alias:具有sudo权限的用户的列表

    2022年6月20日
    30
  • PyCharm之连接MySQL数据库

    PyCharm之连接MySQL数据库1 在 PyCharm 右侧工具栏有 Database 点击打开如果没有 则在 view ToolWindows Database 选择显示 2 点击 database 中的 选择 DataSource 选择 MySQL3 填写远程连接 MySQL 数据库的参数 Host 远程 ip 若是连接本地 MySQL 直接写 localhost 即可 Database

    2026年3月27日
    3
  • 简述JVM垃圾回收机制

    简述JVM垃圾回收机制1.Java中的四种引用类型在Java中,对于引用最基本的解释就是:如果reference类型的数据中存储的数值代表的是另外一块内存的起始地址,就称这块内存代表着一个引用(有点指针的意味)。后来Java还将引用划分为了4种,根据被GC回收的时机可以分为:强引用(StrongReference)、软引用(SoftReference)、弱引用(WeakReference)、虚引用(Phantor

    2022年6月11日
    32
  • php网站开发实例教程源码,PHP+MYSQL网站开发全程实例

    php网站开发实例教程源码,PHP+MYSQL网站开发全程实例1 从观念的引述 程序代码解析到范例的延伸应用 给您最完整的学习流程 2 实务网站范例 所有技术整合运用 实作技巧一览无遗 3 在 PHP 的程序设计中 以高阶的程序设计 配上灵活的图解 增加您程序设计的功力 也能够了解其演算的过程 4 MySQL 数据库设计方面 使用数据库的正规化的方法解决了数据表的分割 来建立关系型数据库 使您有系统的处理现实生活的程序设计 5 深入对象导向程序设计 全新视界

    2026年3月19日
    1
  • decide your linux OS is GUI or not「建议收藏」

    decide your linux OS is GUI or not

    2022年2月4日
    43

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号