Windows线程同步总结

Windows线程同步

Windows的线程同步可以利用互斥对象来实现,也可以使用事件对象,关键代码段来实现。

1 事件对象实现线程同步

<1>Event对象创建函数

事件对象的创建事件对象属于内核对象,它包含以三个成员:使用计数,是否是自动重置还是人工重置的布尔值,通知状态的布尔值。

HANDLE CreateEvent(
         LPSECURITY_ATTRIBUTESlpEventAttributes,
         BOOLbManualReset,
         BOOLbInitialState,
         LPCSTRlpName
);

bManualReset:TRUE代表是人工重置的事件对象,FALSE代表是自动重置的事件对象。如果是人工重置的事件对象,那么当线程等待到该对象的所有权的时候,需要调用ResetEvent函数将该对象的状态设置为无通知状态,并且所有线程均变为可调度线程;如果是自动重置状态,那么当线程得到该对象的所有权的时候,系统会自动的将该对象设置为无通知状态。

bInitialState:TRUE代表事件对象的初始状态是有信号的,FALSE代表初始状态无信号的。

<2>设置信号的状态

信号的状态可以设置为有信号和无信号两种状态,有信号的设置需要调用函数

BOOL        SetEvent(HANDLE hEvent);

设置为无信号需要调用下面的函数:

BOOL        ResetEvent(HANDLE        hEvent);

<3>利用事件对象实现线程同步的条件

条件就是bManualReset参数必须设置为FALSE,即自动重置的事件对象。

分析:

如果设置bManualReset对象为TRUE的话,此时如果把bInitialState设置为FALSE的话,那么等待事件对象的子线程永远等待不了Event对象变为有信号的状态,因为没有人工重置的话它是不会变成有信号的状态的。

如果把bInitialState设置为TRUE的话,那么子线程都可以去竞争事件的所有权,即便一个线程获得了所有权,但是事件对象是始终有信号的,所以另外的线程可以随时的切换过去,这样也是不可取的。

那么如果在线程获取了对象的所有权后立即ResetEvent将事件对象变成无信号的可行不?答案是不行,因为可能存在这样的情况:就是信号还没来得急改变,时间片已经转到另一个线程了,因为此时的信号是处于通知状态的,那么被切换到的线程也可以获取Event对象的所有权,然后进入它的受保护代码区,这样就造成了不止一个线程同时进入了它们各自的受保护代码区,从而破坏了同步。

结论:

如果想利用Event对象来实现线程同步,那么就必须将bManualReset参数设置为FALSE,即该Event对象时自动重置对象。

示例程序:

#include <iostream>
#include <Windows.h>
using namespace std;

HANDLE gEvent =NULL;
int     tickets= 100;

DWORD   WINAPI fun1Proc(LPVOID lpParameter);
DWORD   WINAPI fun2Proc(LPVOID lpParameter);
void main()
{
         HANDLE hThread1 = NULL;
         HANDLE hThread2 = NULL;

         //gEvent =CreateEvent(NULL,TRUE,TRUE,NULL);
         gEvent =CreateEvent(NULL,FALSE,TRUE,NULL);
         CreateThread(NULL,0,fun1Proc,NULL,0,NULL);
         CreateThread(NULL,0,fun2Proc,NULL,0,NULL);
         CloseHandle(hThread1);
         CloseHandle(hThread2);
         Sleep(4000);
         CloseHandle(gEvent);
}

DWORD   WINAPI fun1Proc(LPVOID lpParameter)
{
         while(TRUE)
         {
                   WaitForSingleObject(gEvent,INFINITE);
                   //ResetEvent(gEvent);
                   if(tickets>0 )
                   {
                            Sleep(1);
                            cout<<"thread 1 is selling tickets:"<<tickets--<<endl;
                            SetEvent(gEvent);
                   }
                   else
                   {
                            SetEvent(gEvent);
                            break;
                   }
         }
         return0;
}
DWORD   WINAPI fun2Proc(LPVOID lpParameter)
{
         while(TRUE)
         {
                   WaitForSingleObject(gEvent,INFINITE);
                   //ResetEvent(gEvent);
                   if(tickets>0 )
                   {
                            Sleep(1);
                            cout<<"thread 2 is selling tickets:"<<tickets--<<endl;
                            SetEvent(gEvent);
                   }
                   else
                   {
                            SetEvent(gEvent);
                            break;
                   }
         }
         return0;
}

2 关键代码段

关键代码段也被称为临界区,工作在用户模式下。它是指一个小代码段,在代码能够执行前,它必须独占某些资源的访问权。

它的流程如下所示:

示例代码:

#include <iostream>
#include <Windows.h>
using namespace std;

DWORD   WINAPI fun1Proc(LPVOID lpParameter);
DWORD   WINAPI fun2Proc(LPVOID lpParameter);

CRITICAL_SECTIONgCs;
int tickets = 100;
void main()
{
         HANDLE hThread1 = NULL;
         HANDLE hThread2 = NULL;

         hThread1 =CreateThread(NULL,0,fun1Proc,NULL,0,NULL);
         hThread2 =CreateThread(NULL,0,fun2Proc,NULL,0,NULL);
         CloseHandle(hThread1);
         CloseHandle(hThread2);
         InitializeCriticalSection(&gCs);
         Sleep(4000);
         DeleteCriticalSection(&gCs);
}

DWORD   WINAPI   fun1Proc(LPVOIDlpParameter)
{
         while(TRUE)
         {
                   EnterCriticalSection(&gCs);
                   //Sleep(1);
                   if(tickets>0)
                   {
                            cout<<"thread1 is selling ticket:"<<tickets--<<endl;
                            LeaveCriticalSection(&gCs);
                   }
                   else
                   {
                            LeaveCriticalSection(&gCs);
                            break;
                   }
         }
         return0;
}

DWORD   WINAPI   fun2Proc(LPVOIDlpParameter)
{
         while(TRUE)
         {
                   EnterCriticalSection(&gCs);
                   //Sleep(1);
                   if(tickets>0)
                   {
                            cout<<"thread2 is selling ticket:"<<tickets--<<endl;
                            LeaveCriticalSection(&gCs);
                   }
                   else
                   {
                            LeaveCriticalSection(&gCs);
                            break;
                   }
         }
         return0;
}

3 三种线程同步方式的比较

<1>Mutex,Event属于内核对象,内核对象在进行线程同步的时候速度较慢

<2>CriticalSection属于用户方式,同步速度慢

<3>Mutex,Event同步的时候需要用到WaitForSingleObject来等待信号变为有效的时候进入核心代码段,等处理完了时候需要将信号设置为有信号状态以便其它线程进入核心代码段。

CriticalSection则是用了比较形象的方式EnterCriticalSection以及LeaveCriticalSection。

<4>Mutex,Event在最终需要CloseHandle来关闭内核对象,CriticalSection则是用DeleteCriticalSection来删除对象。

<5>总体来说三种方式都大同小异,都要用一个标志或者别的方式来宣称自己独占资源,然后在使用完资源的后再释放所有权。

时间: 2024-10-10 21:35:56

Windows线程同步总结的相关文章

Windows线程同步

说到windows线程同步的方法,有循环锁,临界区(关键代码段),内核对象(Event,Semaphore,Mutex).甚至进程,线程handle等等. 说说用法: 临界区和mutex都可以用于控制共享资源的互斥访问,不同点是 临界区是用户级对象,消耗小,速度快,但是不能跨进程.mutex是内核对象速度慢,但是可以跨进程. semaphore可以用来进行资源控制,信号量的两个关键参数,现有资源数和最大资源数,可以用于对资源的控制,通过ReleaseSemaphore对资源增加,WaitFor*

windows线程同步之原子锁(原子访问)

原子锁(原子访问):一个线程在访问某个资源的同时必须确保其他线程不会同时访问此资源. 没有实现原子锁的结果: //定义一个全局变量 long g_lx = 0; DWORD WINAPI ThreadFunc1(PVOID pvParam){ for( int index=0; index<10000; index++ ) { g_lx++: //g_lx加1: } return 0: } DWORD WINAPI ThreadFunc2(PVOID pvParam){ for( int ind

Windows线程同步(未完)

先介绍一个创建线程的API,参考:https://msdn.microsoft.com/en-us/library/windows/desktop/ms682453%28v=vs.85%29.aspx Creates a thread to execute within the virtual address space of the calling process. HANDLE WINAPI CreateThread( _In_opt_ LPSECURITY_ATTRIBUTES lpThr

windows 线程同步学习测试-1

环境win7旗舰64位系统,vs2013,AMD fx™4100 Auad-core processor ,8G内存, 看<windows核心编程>线程同步一章,看到有说g_x++会不同步的问题,试着写些代码加深印象.发现+1太快了,看不出效果,于是for循环了1亿次.代码如下: #include "stdafx.h" using std::cout; using std::endl; using std::cin; unsigned __stdcall ThreadFun

Windows线程同步【2】临界区

临界区是线程同步的一种实现方式.Windows提供了4个关于临界区的函数(InitializeCriticalSection,EnterCriticalSection,LeaveCriticalSection,DeleteCriticalSection),要想使用这些函数,必须先有一个临界区变量, CRITICAL_SECTION cs; 临界区变量,不能复制,不能移动,也不能读取里面的字段(可以取临界区变量的地址).总而言之,我们在编写程序时,必须把临界区变量当成一个黑箱,一切对临界区的操作必

windows 线程同步

Windows 临界区,内核事件,互斥量,信号量. 临界区,内核事件,互斥量,信号量,都能完成线程的同步,在这里把他们各自的函数调用,结构定义,以及适用情况做一个总结. 临界区: 适用范围:它只能同步一个进程中的线程,不能跨进程同步.一般用它来做单个进程内的代码快同步,效率比较高. 相关结构:CRITICAL_SECTION  _critical 相关方法: /*初始化,最先调用的函数.没什么好说的,一般windows编程都有类似初始化的方法*/ InitializeCriticalSectio

Windows线程同步【3】互斥锁(Mutex)

我们前面讲过的临界区,如同一个小房间,张三进去了,李四就不能进,如果李四要进,必须等张三出来. 今天我们要讲的互斥锁,像一个物件,这个物件只能同时被一个线程持有.如此一来,便可以通过互斥锁来实现线程的同步. 一.创建 创建互斥锁的方法是调用函数CreateMutex: CreateMutex(&sa, bInitialOwner, szName); 第一个参数是一个指向SECURITY_ATTRIBUTES结构体的指针,一般的情况下,可以是nullptr. 第二个参数类型为BOOL,表示互斥锁创

Windows线程同步【5】条件变量(Condition Variable)

一.引言 假设有一个任务,由我和张三共同完成.张三把寄来的文稿初步审阅后放入一个队列,我负责将这个队列中的文稿进行审批,决定刊登与否.张三审阅一份文稿需要15分钟,我处理一个文稿需要2分钟. 如果将张三和我看作两个线程,那么我们共享一个队列的数据.按照一般的多线程思路,他每隔一段时间往队列中放入数据,我每隔一段时间检查一下队列中是否有数据,若有,则处理之. 若我们按照上面的方式工作,则大部分的时间,我只是在干等着,所以,这是一种比较低效的方式. 但换一种方式之后,情况就好很多了.他每把一个文稿放

Windows线程同步【4】读写锁(RWMutex)

在视频播放机程序中,经常采用这样的架构: 一个负责读文件的线程,负责从媒体文件中读取数据并将数据包放入一个包队列(queue),另外有多个线程从这个包队列中取包并解码,然后交给负责显示的线程以便显示图像.于是,便形成了多个线程共享一个队列,一个写,多个只读不写的情形. 在很多时候,我们都会遇到一个写线程(负责写数据),多个读线程(负责读数据)的情形.在这类情形下,当然可以用锁来保证每个线程对共享数据的访问是独占的.但这样的做法在此情形下是低效的,因为它会使得一个读线程在读数据时另外的读线程只能干