当前位置:首页 » 版权产权 » 临界区线程所有权

临界区线程所有权

发布时间: 2021-03-02 20:14:17

① windows中使用临界区需不需要切换内核态

临界区是一种轻量级机制,在某一时间内只允许一个线程执行某个给定代码段。通常在多线程修改全局数据时会使用临界区。事件、信号量也用于多线程同步,但临界区与它们不同,并不总是执行向内核模式的切换,这一转换成本昂贵。要获得一个未占用临界区,事实上只需要对内存做出很少的修改,其速度非常快。只有在尝试获得已占用临界区时,它才会跳至内核模式。这一轻量级特性的缺点在于临界区只能用于对同一进程内的线程进行同步。
临界区由 WINNT.H 中所定义的 RTL_CRITICAL_SECTION 结构表示。 WINBASE.H 后您会发现:
typedef RTL_CRITICAL_SECTION CRITICAL_SECTION;
操作临界区的API函数有:
(1)初始化临界区InitializeCriticalSection
(2)进入临界区EnterCriticalSection
(3)离开临界区LeaveCriticalSection
(4)删除临界区DeleteCriticalSection
在临界区未被使用的理想情况中,对 EnterCriticalSection 的调用非常快速,因为它只是读取和修改用户模式内存中的内存位置。所阻止的线程以内核模式等待,在该临界区的所有者将其释放之前,不能对这些线程进行调度。如果有多个线程被阻止于一个临界区中,当另一线程释放该临界区时,只有一个线程获得该临界区。
RTL_CRITICAL_SECTION 结构
一个进程的临界区是保存于一个链表中,并且可以对其进行枚举。实际上,WINDBG 支持 !locks 命令,这一命令可以列出目标进程中的所有临界区。
RTL_CRITICAL_SECTION 结构如下:
struct RTL_CRITICAL_SECTION
{
PRTL_CRITICAL_SECTION_DEBUG DebugInfo;
LONG LockCount;
LONG RecursionCount;
HANDLE OwningThread;
HANDLE LockSemaphore;
ULONG_PTR SpinCount;
};
以下各段对每个字段进行说明。
DebugInfo 此字段包含一个指针,指向系统分配的伴随结构,该结构的类型为 RTL_CRITICAL_SECTION_DEBUG。这一结构中包含更多极有价值的信息,也定义于 WINNT.H 中。LockCount 这是临界区中最重要的一个字段。它被初始化为数值 -1;此数值等于或大于 0 时,表示此临界区被占用。当其不等于 -1 时,OwningThread 字段包含了拥有此临界区的线程 ID。此字段与 (RecursionCount-1) 数值之间的差值表示有多少个其他线程在等待获得该临界区。
RecursionCount 此字段包含所有者线程已经获得该临界区的次数。如果该数值为零,下一个尝试获取该临界区的线程将会成功。
OwningThread 此字段包含当前占用此临界区的线程的线程标识符。此线程 ID 与 GetCurrentThreadId 之类的 API 所返回的 ID 相同。
LockSemaphore 它是一个内核对象句柄,用于通知操作系统:该临界区现在空闲。操作系统在一个线程第一次尝试获得该临界区,但被另一个已经拥有该临界区的线程所阻止时,自动创建这样一个句柄。应当调用 DeleteCriticalSection(它将发出一个调用该事件的 CloseHandle 调用,并在必要时释放该调试结构),否则将会发生资源泄漏。
SpinCount 仅用于多处理器系统。MSDN文档对此字段进行如下说明:“在多处理器系统中,如果该临界区不可用,调用线程将在对与该临界区相关的信号执行等待操作之前,旋转 dwSpinCount 次。如果该临界区在旋转操作期间变为可用,该调用线程就避免了等待操作。”旋转计数可以在多处理器计算机上提供更佳性能,其原因在于在一个循环中旋转通常要快于进入内核模式等待状态。此字段默认值为零,但可以用 API 将其设置为一个不同值。
RTL_CRITICAL_SECTION_DEBUG结构如下:
struct _RTL_CRITICAL_SECTION_DEBUG
{
WORD Type;
WORD CreatorBackTraceIndex;
RTL_CRITICAL_SECTION *CriticalSection;
LIST_ENTRY ProcessLocksList;
DWORD EntryCount;
DWORD ContentionCount;
DWORD Spare[ 2 ];
}
这一结构由InitializeCriticalSection分配和初始化。它既可以由NTDLL内的预分配数组分配,也可以由进程堆分配。RTL_CRITICAL_SECTION的这一伴随结构包含一组匹配字段,具有迥然不同的角色:有两个难以理解,随后两个提供了理解这一临界区链结构的关键,两个是重复设置的,最后两个未使用。
下面是对 RTL_CRITICAL_SECTION 字段的说明。
Type 此字段未使用,被初始化为数值 0。
CreatorBackTraceIndex 此字段仅用于诊断情形中。在注册表项 HKLM\Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\YourProgram 之下是 keyfield、GlobalFlag 和 StackTraceDatabaseSizeInMb 值。注意,只有在运行稍后说明的 Gflags 命令时才会显示这些值。这些注册表值的设置正确时,CreatorBackTraceIndex 字段将由堆栈跟踪中所用的一个索引值填充。在 MSDN 中搜索 GFlags 文档中的短语“create user mode stack trace database”和“enlarging the user-mode stack trace database”,可以找到有关这一内容的更多信息。
CriticalSection 指向与此结构相关的 RTL_CRITICAL_SECTION。图 1 说明该基础结构以及 RTL_CRITICAL_SECTION、RTL_CRITICAL_SECTION_DEBUG 和事件链中其他参与者之间的关系。

图 1 临界区处理流程
ProcessLocksList LIST_ENTRY 是用于表示双向链表中节点的标准 Windows 数据结构。RTL_CRITICAL_SECTION_DEBUG 包含了链表的一部分,允许向前和向后遍历该临界区。本文后面给出的实用工具说明如何使用 Flink(前向链接)和 Blink(后向链接)字段在链表中的成员之间移动。任何从事过设备驱动程序或者研究过 Windows 内核的人都会非常熟悉这一数据结构。
EntryCount/ContentionCount 这些字段在相同的时间、出于相同的原因被递增。这是那些因为不能马上获得临界区而进入等待状态的线程的数目。与 LockCount 和 RecursionCount 字段不同,这些字段永远都不会递减。
Spares 这两个字段未使用,甚至未被初始化(尽管在删除临界区结构时将这些字段进行了清零)。后面将会说明,可以用这些未被使用的字段来保存有用的诊断值。
总结:
(1)如果 LockCount 字段有一个不等于 -1 的数值,此临界区被占用,OwningThread 字段包含拥有该临界区的线程的线程标识符。
(2)如果 RecursionCount 是一个大于 1 的数值,其告知您所有者线程已经重新获得该临界区多少次(也许不必要)。
(3)LockCount 与 RecursionCount 字段中分别包含其初始值 -1 和 0,这一点非常重要。事实上,对于单线程程序,不能仅通过检查这些字段来判断是否曾获得过临界区。但是,多线程程序留下了一些标记,可以用来判断是否有两个或多个线程试图同时拥有同一临界区。
(4)在该临界区未被占用时 LockSemaphore 字段中仍包含一个非零值。这表示:在某一时间,此临界区阻止了一个或多个线程。事件句柄用于通知该临界区已被释放,等待该临界区的线程之一现在可以获得该临界区并继续执行。因为 OS 在临界区阻止另一个线程时自动分配事件句柄,所以如果您在不再需要临界区时忘记将其删除,LockSemaphore 字段可能会导致程序中发生资源泄漏。
(5)在多线程程序中可能遇到的另一状态是 EntryCount 和 ContentionCount 字段包含一个大于零的数值。这两个字段保存有临界区对一个线程进行阻止的次数。在每次发生这一事件时,这两个字段被递增,但在临界区存在期间不会被递减。这些字段可用于间接确定程序的执行路径和特性。例如,EntryCount 非常高时则意味着该临界区经历着大量争用,可能会成为代码执行过程中的一个潜在瓶颈。
(6)可以通过RTL_CRITICAL_SECTION_DEBUG 中的LIST_ENTRY 遍历进程中的临界区,Flink=NULL为表头,Blink=NULL为表尾。
(7)利用RTL_CRITICAL_SECTION 的 Spare 字段可以区分我们定义的临界区和系统定义的临界区。

② 临界区的临界区存在的几个问题

在使用临界区时,一般不允许其运行时间过长,只要进入临界区的线程还没有离开,其他所有试图进入此临界区的线程都会被挂起而进入到等待状态,并会在一定程度上影响程序的运行性能。尤其需要注意的是不要将等待用户输入或是其他一些外界干预的操作包含到临界区。如果进入了临界区却一直没有释放,同样也会引起其他线程的长时间等待。换句话说,在执行了EnterCriticalSection()语句进入临界区后无论发生什么,必须确保与之匹配的LeaveCriticalSection()都能够被执行到。可以通过添加结构化异常处理代码来确保LeaveCriticalSection()语句的执行。虽然临界区同步速度很快,但却只能用来同步本进程内的线程,而不可用来同步多个进程中的线程。
1、 临界区的退出,不会检测是否是已经进入的线程,也就是说,我可以在A线程中调用进入临界区函数,在B线程调用退出临界区的函数,同样是成功;
2、 我在测试临界区的时候,如果我没有调用进入临界区的函数,直接退出的话,系统没有进行判断,但是计数发现了改变,此时此临界区就再也用不了了,因为结构中的数据已经乱掉了。
解决方法如下:
ypedef class mutex_lock
{
public:
mutex_lock()
: LockCount(-1)
, hEvent(0)
{
}
~mutex_lock()
{
if(NULL != this->hEvent)
{
CloseHandle(this->hEvent);
}
this->hEvent = NULL;
this->LockCount = -1;
}
long GetLock();
long ReleaseLock();
private:
mutex_lock(mutex_lock&);
long LockCount;
HANDLE hEvent;
} MUTEXLOCK, *LPMUTEXLOCK;
long mutex_lock::GetLock()
{
__asm
{
movebx, [this];//把基址保存在ebx中
lock incdword ptr [ebx];//对LockCount进行加锁加,保证多CPU时的唯一性,
jeLRET1;//如果LockCount加1为0的话,表示没有人在使用资源,同时利用上面的互斥加对资源进行占用。
cmpdword ptr [ebx+4], 0;//此时LockCount加1大于0的情况,表示已有人使用此资源,要对此资源进行加内核锁
jneL1;//如果平常没有创建内核锁,则进行创始一个
push 0;
push 0;
push 0;
push 0;
call dword ptr [CreateEvent];
movedx, eax;//创建内存锁成功后,在同样对hEvent变量进行互斥比较后替换,以处理多个线程同时对此值进行替换的情况,保证只有一个能够成功替换
moveax, 0;
lock cmpxchg dword ptr [ebx+4], edx; //互斥比较替换
jeL1;
push edx;//如果已经被别人替换过了,需要把自己创建的内核锁释放
call dword ptr [CloseHandle];
L1:
push INFINITE;
push [ebx+4];
call dword ptr [WaitForSingleObject]; //在内核级进行等待
LRET1:
moveax, 0
}
}
long mutex_lock::ReleaseLock()
{
__asm
{
movebx, [this];//把基址保存在ebx中
moveax, -1;
lock xadd dword ptr [ebx], eax; //进行交换自减交换操作,运行后,eax中是第一操作数先前的值,此值用会返回用来判断是否多调用了ReleaseLock
jlLRET2;//没有别的线程占用资源,直接返回,用户可以通过返回值,分析是否失败
cmpdword ptr [ebx+4], 0;//有别的线程在等待,检查内核锁,如果没有则进行创建
jneL2;
push 0;
push 0;
push 0;
push 0;
call dword ptr [CreateEvent];
movedx, eax;
moveax, 0;
lock cmpxchg dword ptr [ebx+4], edx; //互斥替换内核锁句柄
jeL2;//已经有内核锁了,把自已申请的关闭
push edx;
call dword ptr [CloseHandle];
L2:
push [ebx+4];//设计信号,唤醒一个线程
call dword ptr [SetEvent];
moveax, 0;
LRET2:
}
}

③ 多线程里面的临界区如何使用

一般用于多个线程要跑同一个函数,这个函数又要操作一个资源的时候。。。

比如你有个生产电脑的流水线,多个线程都要调用这个流水线生产电脑,那么在生产过程中可能就需要用临界区确保每次只有一台电脑在生产。

④ 多线程的临界区域,是如何设定的呢

使用四个函数,InitializeCriticalSection()初始化临界区回,EnterCriticalSection()进入临界区,LeaveCriticalSection()离开答临界区,DeleteCriticalSection()删除临界区。

⑤ linux多线程 有 临界区吗

临界区是自己创建的

⑥ java线程如何保护临界区

每个进程中访问临界资源的那段程序称为临界区(Critical Section)(临界资源是一次仅允许一个进内程使用的容共享资源)。每次只准许一个进程进入临界区,进入后不允许其他进程进入。不论是硬件临界资源,还是软件临界资源,多个进程必须互斥地对它进行访问。 多个进程中涉及到同一个临界资源的临界区称为相关临界区。 进程进入临界区的调度原则是: ①如果有若干进程要求进入空闲的临界区,一次仅允许一个进程进入。②任何时候,处于临界区内的进程不可多于一个。如已有进程进入自己的临界区,则其它所有试图进入临界区的进程必须等待。③进入临界区的进程要在有限时间内退出,以便其它进程能及时进入自己的临界区。④如果进程不能进入自己的临界区,则应让出CPU,避免进程出现“忙等”现象。 如果有多个线程试图同时访问临界区,那么在有一个线程进入后其他所有试图访问此临界区的线程将被挂起,并一直持续到进入临界区的线程离开。临界区在被释放后,其他线程可以继续抢占,并以此达到用原子方式操作共享资源的目的。

⑦ 简述什么是线程同步机制和临界区管理

临界抄区的基本概念

临界区:在同一个进程内的多个线程之间通过原子方式实现共享资源的串行化读写。

临界区相关函数

CRITICAL_SECTION cs;

::InitializeCriticalSection(&cs);

::EnterCriticalSection(&cs);

::LeaveCriticalSection(&cs);

::DeleteCriticalSection(&cs);

临界区编程实例

实例一:临界区基本使用方法

输出结果(注掉13,17,没有进行临界区同步):

输出结果(临界区同步):

实例二:使用临界区对象创建自动锁

CriticalSection:封装 Windows CRICITAL_SECTION 用户对象

AutoLock:封装CriticalSection的操作,让它进入一个Scope的时候自动加锁,离开一个Scope的时候自动解锁

⑧ 什么时候使用互斥体,什么时候用临界区

互斥体实现了“互相排斥”(mutual exclusion)同步的简单形式(所以名为互斥体(mutex))。互回斥体禁止多个线程同时进入答受保护的代码“临界区”(critical section)。
每个进程中访问临界资源的那段代码称为临界区(Critical Section)(临界资源是一次仅允许一个进程使用的共享资源)。每次只准许一个进程进入临界区,进入后不允许其他进程进入。不论是硬件临界资源,还是软件临界资源,多个进程必须互斥地对它进行访问。
多个进程中涉及到同一个临界资源的临界区称为相关临界区。.
在任意时刻,只有一个线程被允许进入这样的代码保护区。任何线程在进入临界区之前,必须获取(acquire)与此区域相关联的互斥体的所有权。如果已有另一线程拥有了临界区的互斥体,其他线程就不能再进入其中。这些线程必须等待,直到当前的属主线程释放(release)该互斥体。什么时候需要使用互斥体呢?互斥体用于保护共享的易变代码,也就是,全局或静态数据。这样的数据必须通过互斥体进行保护,以防止它们在多个线程同时访问时损坏。

⑨ 临界区的线程同步问题

有多个线程试图同时访问临界区,那么在有一个线程进入后其他所有试图访问此临界区的线程将被挂起,并一直持续到进入临界区的线程离开。临界区在被释放后,其他线程可以继续抢占,并以此达到用原子方式操作共享资源的目的。
临界区在使用时以CRITICAL_SECTION结构对象保护共享资源,并分别用EnterCriticalSection()和LeaveCriticalSection()函数去标识和释放一个临界区。所用到的CRITICAL_SECTION结构对象必须经过InitializeCriticalSection()的初始化后才能使用,而且必须确保所有线程中的任何试图访问此共享资源的代码都处在此临界区的保护之下。否则临界区将不会起到应有的作用,共享资源依然有被破坏的可能。
下面通过一段代码展示了临界区在保护多线程访问的共享资源中的作用。通过两个线程来分别对全局变量g_cArray[10]进行写入操作,用临界区结构对象g_cs来保持线程的同步,并在开启线程前对其进行初始化。为了使实验效果更加明显,体现出临界区的作用,在线程函数对共享资源g_cArray[10]的写入时,以Sleep()函数延迟1毫秒,使其他线程同其抢占CPU的可能性增大。如果不使用临界区对其进行保护,则共享资源数据将被破坏(参见图1(a)所示计算结果),而使用临界区对线程保持同步后则可以得到正确的结果(参见图1(b)所示计算结果)。
代码实现清单附下: //临界区结构对象CRITICAL_SECTIONg_cs;//共享资源charg_cArray[10];UINTThreadProc10(LPVOIDpParam){//进入临界区EnterCriticalSection(&g_cs);//对共享资源进行写入操作for(inti=0;i<10;i++){g_cArray[i]=a;Sleep(1);}//离开临界区LeaveCriticalSection(&g_cs);return0;}UINTThreadProc11(LPVOIDpParam){//进入临界区EnterCriticalSection(&g_cs);//对共享资源进行写入操作for(inti=0;i<10;i++){g_cArray[10-i-1]=b;Sleep(1);}//离开临界区LeaveCriticalSection(&g_cs);return0;}……voidCSample08View::OnCriticalSection(){//初始化临界区InitializeCriticalSection(&g_cs);//启动线程AfxBeginThread(ThreadProc10,NULL);AfxBeginThread(ThreadProc11,NULL);//等待计算完毕Sleep(300);//报告计算结果CStringsResult=CString(g_cArray);AfxMessageBox(sResult);}

⑩ 何谓临界区下面给出的两个进程互斥的算法是安全的吗为什么

临界区就是只有一个线程能进去的区域,进程互斥可以说是安全的,因为不同的进程不共享内存。
但是也有用到互斥的地方,比如文件锁,
消息队列等

热点内容
美发店认证 发布:2021-03-16 21:43:38 浏览:443
物业纠纷原因 发布:2021-03-16 21:42:46 浏览:474
全国著名不孕不育医院 发布:2021-03-16 21:42:24 浏览:679
知名明星确诊 发布:2021-03-16 21:42:04 浏览:14
ipad大专有用吗 发布:2021-03-16 21:40:58 浏览:670
公务员协议班值得吗 发布:2021-03-16 21:40:00 浏览:21
知名书店品牌 发布:2021-03-16 21:39:09 浏览:949
q雷授权码在哪里买 发布:2021-03-16 21:38:44 浏览:852
图书天猫转让 发布:2021-03-16 21:38:26 浏览:707
宝宝水杯品牌 发布:2021-03-16 21:35:56 浏览:837