首页
论坛
课程
招聘
[原创]用户态线程同步各技术分析
2017-12-21 21:06 2531

[原创]用户态线程同步各技术分析

2017-12-21 21:06
2531

0. Brief introduce

A critical section object provides synchronization similar to that provided by a mutex object, except that a critical section can be used only by the threads of a single process. Event, mutex, and semaphore objects can also be used in a single-process application, but critical section objects provide a slightly faster, more efficient mechanism for mutual-exclusion synchronization (a processor-specific test and set instruction). Like a mutex object, a critical section object can be owned by only one thread at a time, which makes it useful for protecting a shared resource from simultaneous access. Unlike a mutex object, there is no way to tell whether a critical section has been abandoned.

 

临界区是一个进程内的线程间的轻量的互斥对象.拥有者线程代码一直是有信号的,其它线程没有信号.拥有者线程代码每进一次临界区就会累加锁,退出临界区就会累减锁.

1. Data structure

typedef RTL_CRITICAL_SECTION CRITICAL_SECTION;

typedef struct _RTL_CRITICAL_SECTION {
    PRTL_CRITICAL_SECTION_DEBUG DebugInfo;
    LONG LockCount;             // 
    LONG RecursionCount;        // 拥有者线程正在使用临界区的计数
    HANDLE OwningThread;        // 拥有临界区对象的线程的ID
    HANDLE LockSemaphore;       // 是一个auto-reset的事件句柄,用于唤醒等待获取临界区的阻塞线程
    ULONG_PTR SpinCount;        // force size on 64-bit systems when packed
} RTL_CRITICAL_SECTION, *PRTL_CRITICAL_SECTION;

typedef struct _RTL_CRITICAL_SECTION_DEBUG {
    WORD                         Type;
    WORD                         CreatorBackTraceIndex;
    struct _RTL_CRITICAL_SECTION *CriticalSection;
    LIST_ENTRY                   ProcessLocksList;
    DWORD                        EntryCount;
    DWORD                        ContentionCount;
    DWORD                        Flags;
    WORD                         CreatorBackTraceIndexHigh;
    WORD                         SpareWORD;
} RTL_CRITICAL_SECTION_DEBUG, *PRTL_CRITICAL_SECTION_DEBUG, RTL_RESOURCE_DEBUG, *PRTL_RESOURCE_DEBUG;

2. Operate function

void WINAPI InitializeCriticalSection(_Out_ LPCRITICAL_SECTION lpCriticalSection);
void WINAPI DeleteCriticalSection(_Inout_ LPCRITICAL_SECTION lpCriticalSection);

void WINAPI EnterCriticalSection(_Inout_ LPCRITICAL_SECTION lpCriticalSection);
void WINAPI LeaveCriticalSection(_Inout_ LPCRITICAL_SECTION lpCriticalSection);

3. Design

临界区有线程拥有权概念
临界区的线程拥有权只体现在互斥上.当临界区被一个线程暂时拥有,其它访问它的线程将会睡眠.当临界区成为无主之物时,其它线程之一会拥有临界区.
临界区可被多次上锁

 

主要体现在_RTL_CRITICAL_SECTION::RecursionCount字段.当临界区被一个线程暂时拥有,线程可以作用多次EnterCriticalSection()给临界区对象上锁.如果没能用对应次数LeaveCriticalSection()去解锁,那么临界区对象不会被释放.其它线程也就不能得到临界区对象了.

 

其它线程可释放临界区对象.

 

临界区是用户态进程对象

 

正常情况下,临界区只能用来互斥进程内的线程.

 

对其它程序临界区对象是不可见的.

 

在同一线程内,临界区毫无作用.

 

系统不会处理无效临界区对象

 

如果一个临界区对象拥有者线程意外结束,没有能释放临界区,临界区对象也就锁死了.

4. Summary

  1. 临界区是用户态对象.
  2. 临界区有线程拥有权概念:线程的互斥.
  3. 系统不会处理无效临界区.
  4. 其它线程可释放临界区对象.
  5. 有无信号是对线程而言的.
  6. 递归计数是对拥有者线程而言的.
  7. 递归计数可以由任何线程减小.
  8. 其它等待线程会被睡眠,在适当时机被唤醒.
  9. 临界区适用于进程内线程间的互斥.只在一个线程中使用的临界区没有存在的理由.

0. Brief introduce

A mutex object is a synchronization object whose state is set to signaled when it is not owned by any thread, and nonsignaled when it is owned. Only one thread at a time can own a mutex object, whose name comes from the fact that it is useful in coordinating mutually exclusive access to a shared resource. For example, to prevent two threads from writing to shared memory at the same time, each thread waits for ownership of a mutex object before executing the code that accesses the memory. After writing to the shared memory, the thread releases the mutex object.

 

互斥体是内核对象.拥有者线程代码一直是有信号的,其它线程没有信号.拥有者线程代码每进一次互斥区就会累加锁,退出互斥区就会累减锁.它与临界区十分相似.

1. Data structure

2. Operate function

HANDLE WINAPI CreateMutex(_In_opt_ LPSECURITY_ATTRIBUTES lpMutexAttributes, _In_ BOOL bInitialOwner, _In_opt_ LPCTSTR lpName);
HANDLE WINAPI OpenMutex(_In_ DWORD dwDesiredAccess, _In_ BOOL bInheritHandle, _In_ LPCTSTR lpName);
BOOL WINAPI CloseHandle(_In_ HANDLE hObject);

DWORD WINAPI WaitForSingleObject(_In_ HANDLE hHandle, _In_ DWORD dwMilliseconds);    // 会让互斥体对其它线程没有信号
BOOL WINAPI ReleaseMutex(_In_ HANDLE hMutex);

3. Design

互斥体有线程拥有权概念

 

互斥体的线程拥有权体现在互斥&释放上.只有拥有者线程才能释放互斥体对象.

 

互斥体可被多次上锁

 

只有拥有者线程可释放互斥体对象.

 

互斥体是内核对象

 

系统会处理无效互斥体对象

 

如果一个互斥体对象拥有者线程结束,没有能释放互斥体对象,系统会释放该互斥体对象,使其有信号.

4. Summary

  1. 互斥体是内核对象.
  2. 互斥体有线程拥有权概念:线程的互斥,递归计数的减小.
  3. 系统会处理无效互斥体,一个结束的线程一定会释放拥有互斥体对象.
  4. 只有拥有者线程可释放互斥体对象.
  5. 有无信号是对线程而言的.
  6. 递归计数是对拥有者线程而言的.
  7. 递归计数只能由拥有者线程减小.
  8. 其它等待线程会被睡眠,在适当时机被唤醒.
  9. 互斥体适用于线程间的互斥.只在一个线程中使用的互斥体没有存在的理由.

0. Brief introduce

A semaphore object is a synchronization object that maintains a count between zero and a specified maximum value. The count is decremented each time a thread completes a wait for the semaphore object and incremented each time a thread releases the semaphore. When the count reaches zero, no more threads can successfully wait for the semaphore object state to become signaled. The state of a semaphore is set to signaled when its count is greater than zero, and nonsignaled when its count is zero.

 

这上面的表达与Mutex有一个很大的区别:==each time==与==at a time==.信号量是没有递归计数的.每进一次信号区就会累减计数,退出信号区就会累加计数.

1. Data structure

2. Operate function

HANDLE WINAPI CreateSemaphore(_In_opt_ LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, _In_ LONG lInitialCount, _In_ LONG lMaximumCount, _In_opt_ LPCTSTR lpName);
HANDLE WINAPI OpenSemaphore(_In_ DWORD dwDesiredAccess, _In_ BOOL bInheritHandle, _In_ LPCTSTR lpName);
BOOL WINAPI CloseHandle(_In_ HANDLE hObject);

DWORD WINAPI WaitForSingleObject(_In_ HANDLE hHandle, _In_ DWORD dwMilliseconds);    // 会让信号量计数减少
BOOL WINAPI ReleaseSemaphore(_In_ HANDLE hSemaphore, _In_ LONG lReleaseCount, _Out_opt_ LPLONG lpPreviousCount);

3. Design

信号量有线程拥有权概念

 

信号量的线程拥有权只体现在释放上.释放信号量时,只有拥有者线程才能释放信号量.但是拥有者线程可以多个,不大于最大计数.

 

信号量有计数

 

只有拥有者线程可释放临界区对象.

 

信号量是内核对象

 

系统不会处理无效信号量对象

4. Summary

  1. 信号量是内核对象.
  2. 信号量有线程拥有权概念:计数的增加.
  3. 系统不会处理无效信号量.
  4. 只有拥有者线程们可释放对应的自己的信号量对象.
  5. 有无信号是对信号区而言的.
  6. 计数是对所有信号区而言的.
  7. 其它等待线程会被睡眠,在适当时机被唤醒.
  8. 信号量适用于线程间的互斥.只在一个线程中使用的信号量没有存在的理由.

0. Brief introduce

An event object is a synchronization object whose state can be explicitly set to signaled by use of the SetEvent() function. Following are the two types of event object.

Manual-reset event An event object whose state remains signaled until it is explicitly reset to nonsignaled by the ResetEvent()function. While it is signaled, any number of waiting threads, or threads that subsequently specify the same event object in one of the wait functions, can be released.
Auto-reset event An event object whose state remains signaled until a single waiting thread is released, at which time the system automatically sets the state to nonsignaled. If no threads are waiting, the event object's state remains signaled. If more than one thread is waiting, a waiting thread is selected. Do not assume a first-in, first-out (FIFO) order. External events such as kernel-mode APCs can change the wait order.
 

事件是有二种形式的内核对象:手动&自动型信号.它们之间有不少区别.自动设置自带互斥属性,手动设置则反之.

1. Data structure

2. Operate function

HANDLE WINAPI CreateEvent(_In_opt_ LPSECURITY_ATTRIBUTES lpEventAttributes, _In_ BOOL bManualReset, _In_ BOOL bInitialState, _In_opt_ LPCTSTR lpName);
HANDLE WINAPI OpenEvent(_In_ DWORD dwDesiredAccess, _In_ BOOL bInheritHandle, _In_ LPCTSTR lpName);
BOOL WINAPI CloseHandle(_In_ HANDLE hObject);

DWORD WINAPI WaitForSingleObject(_In_ HANDLE hHandle, _In_ DWORD dwMilliseconds);
BOOL WINAPI SetEvent(_In_ HANDLE hEvent);
BOOL WINAPI PulseEvent(_In_ HANDLE hEvent);
BOOL WINAPI ResetEvent(_In_ HANDLE hEvent);

PulseEvent()

Sets the specified event object to the signaled state and then resets it to the nonsignaled state after releasing the appropriate number of waiting threads.

Note: This function is unreliable and should not be used. It exists mainly for backward compatibility. For more information, see Remarks.

A thread waiting on a synchronization object can be momentarily removed from the wait state by a kernel-mode APC, and then returned to the wait state after the APC is complete. If the call to PulseEvent occurs during the time when the thread has been removed from the wait state, the thread will not be released because PulseEvent releases only those threads that are waiting at the moment it is called. Therefore, PulseEvent is unreliable and should not be used by new applications. Instead, use condition variables.

For a manual-reset event object, all waiting threads that can be released immediately are released. The function then resets the event object's state to nonsignaled and returns.

For an auto-reset event object, the function resets the state to nonsignaled and returns after releasing a single waiting thread, even if multiple threads are waiting.

If no threads are waiting, or if no thread can be released immediately, PulseEvent simply sets the event object's state to nonsignaled and returns.

Note that for a thread using the multiple-object wait functions to wait for all specified objects to be signaled, PulseEvent can set the event object's state to signaled and reset it to nonsignaled without causing the wait function to return. This happens if not all of the specified objects are simultaneously signaled.

3. Design

事件是内核对象

 

系统不会处理无效事件对象

4. Summary

  1. 事件是内核对象.
  2. 系统不会处理无效事件.
  3. 其它线程可设置事件为有信号.
  4. 有无信号是对事件区而言的.
  5. 其它等待线程会被睡眠,在适当时机被唤醒.
  6. 事件适用于线程间的同步.
  7. 自动型事件是互斥对象,手动型事件则否.

看雪招聘平台创建简历并且简历完整度达到90%及以上可获得500看雪币~

收藏
点赞0
打赏
分享
最新回复 (2)
雪    币: 713
活跃值: 活跃值 (1347)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
库尔 活跃值 2017-12-21 21:56
2
0
mark
雪    币: 203
活跃值: 活跃值 (14)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
冬天tao 活跃值 2017-12-21 22:11
3
0
Mark,虽然没看懂
游客
登录 | 注册 方可回帖
返回