分享
 
 
 

同步机制及windows同步函数的使用

王朝system·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

本篇文章适合比较熟悉多线程并且想学习线程同步的读者。

最近由于使用多线程,不可避免的要用到线程之间的同步,对一些常用的windows 中同步函数和机制有了一些初步的了解,并且写了一些小例子来验证,当然其中难免有错误和疏漏之处,希望高手能给我这个小鸟指出不足之处,非常感谢。

目录

一 临界区

二 互斥体

三 事件

四 信号量

五 附录

一 临界区

临界区的使用在线程同步中应该算是比较简单,说它简单还是说它同后面讲到的其它方法相比更容易理解。举个简单的例子:比如说有一个全局变量(公共资源)两个线程都会对它进行写操作和读操作,如果我们在这里不加以控制,会产生意想不到的结果。假设线程A正在把全局变量加1然后打印在屏幕上,但是这时切换到线程B,线程B又把全局变量加1然后又切换到线程A,这时候线程A打印的结果就不是程序想要的结果,也就产生了错误。解决的办法就是设置一个区域,让线程A在操纵全局变量的时候进行加锁,线程B如果想操纵这个全局变量就要等待线程A释放这个锁,这个也就是临界区的概念。

使用方法:

CRITICAL_SECTION cs;

InitializeCriticalSection(&cs);

EnterCriticalSection(&cs);

...

LeaveCriticalSection(&cs);

DeleteCriticalSection(&cs);

#include "stdafx.h"

#include <windows.h>

#include <process.h>

#include <iostream>

using namespace std;

/****************************************************************

*在使用临界区的时候要注意,每一个共享资源就有一个CRITICAL_SECTION

*如果要一次访问多个共享变量,各个线程要保证访问的顺序一致,如果不

*一致,很可能发生死锁。例如:

* thread one:

* EnterCriticalSection(&c1)

* EnterCriticalSection(&c2)

* ...

* Leave...

* Leave...

*

* thread two:

* EnterCriticalSection(&c2);

* EnterCriticalSection(&c1);

* ...

* Leave...

* Leave...

*这样的情况就会发生死锁,应该让线程2进入临界区的顺序同线程1相同

****************************************************************/

const int MAX_THREADNUMS = 4; //产生线程数目

CRITICAL_SECTION cs; //临界区

HANDLE event[MAX_THREADNUMS]; //保存createevent的返回handle

int critical_value = 0; //共享资源

UINT WINAPI ThreadFunc(void* arg)

{

int thread = (int)arg;

for (int i = 0; i < 5; i++)

{

EnterCriticalSection(&cs);

cout << "thread " << thread << " ";

critical_value++;

cout << "critical_value = " << critical_value << endl;

LeaveCriticalSection(&cs);

}

SetEvent(event[thread]);

return 1;

}

int main(int argc, char* argv[])

{

cout << "this is a critical_section test program" << endl;

HANDLE hThread;

UINT uThreadID;

DWORD dwWaitRet = 0;

InitializeCriticalSection(&cs);

for (int i = 0; i < MAX_THREADNUMS; i++)

{

event[i] = CreateEvent(NULL, TRUE, FALSE, "");

if (event[i] == NULL)

{

cout << "create event " << i << " failed with code: "

<< GetLastError() << endl;

continue;

}

hThread = (HANDLE)_beginthreadex(NULL, 0, ThreadFunc,

(void*)i, 0, &uThreadID);

if (hThread == 0)

{

cout << "begin thread " << i << " failed with code: "

<< GetLastError() << endl;

continue;

}

CloseHandle(hThread);

}

//等待所有线程完成

dwWaitRet = WaitForMultipleObjects(MAX_THREADNUMS, event, TRUE, INFINITE);

switch(dwWaitRet)

{

case WAIT_OBJECT_0:

cout << "all the sub thread has exit!" << endl;

break;

default:

cout << "wait for all the thread failed with code:" << GetLastError() << endl;

break;

}

DeleteCriticalSection(&cs);

for (int k = 0; k < MAX_THREADNUMS; k++)

{

CloseHandle(event[k]);

}

return 0;

}

二 互斥体

windows api中提供了一个互斥体,功能上要比临界区强大。也许你要问,这个东东和临界区有什么区别,为什么强大?它们有以下几点不一致:

1.critical section是局部对象,而mutex是核心对象。因此像waitforsingleobject是不可以等待临界区的。

2.critical section是快速高效的,而mutex同其相比要慢很多

3.critical section使用范围是单一进程中的各个线程,而mutex由于可以有一个名字,因此它是可以应用于不同的进程,当然也可以应用于同一个进程中的不同线程。

4.critical section 无法检测到是否被某一个线程释放,而mutex在某一个线程结束之后会产生一个abandoned的信息。同时mutex只能被拥有它的线程释放。下面举两个应用mutex的例子,一个是程序只能运行一个实例,也就是说同一个程序如果已经运行了,就不能再运行了;另一个是关于非常经典的哲学家吃饭问题的例子。

程序运行单个实例:

#include "stdafx.h"

#include <windows.h>

#include <process.h>

#include <iostream>

using namespace std;

//当输入s或者c时候结束程序

void PrintInfo(HANDLE& h, char t)

{

char c;

while (1)

{

cin >> c;

if (c == t)

{

ReleaseMutex(h);

CloseHandle(h);

break;

}

Sleep(100);

}

}

int main(int argc, char* argv[])

{

//创建mutex,当已经程序发现已经有这个mutex时候,就相当于openmutex

HANDLE hHandle = CreateMutex(NULL, FALSE, "mutex_test");

if (GetLastError() == ERROR_ALREADY_EXISTS)

{

cout << "you had run this program!" << endl;

cout << "input c to close this window" << endl;

PrintInfo(hHandle, 'c');

return 1;

}

cout << "program run!" << endl;

cout << "input s to exit program" <<endl;

PrintInfo(hHandle, 's');

return 1;

}

哲学家吃饭问题:

const int PHILOSOPHERS = 5; //哲学家人数

const int TIME_EATING = 50; //吃饭需要的时间 毫秒

HANDLE event[PHILOSOPHERS]; //主线程同工作线程保持同步的句柄数组

HANDLE mutex[PHILOSOPHERS]; //mutex数组,这里相当于公共资源筷子

CRITICAL_SECTION cs; //控制打印的临界区变量

UINT WINAPI ThreadFunc(void* arg)

{

int num = (int)arg;

DWORD ret = 0;

while (1)

{

ret = WaitForMultipleObjects(2, mutex, TRUE, 1000);

if (ret == WAIT_TIMEOUT)

{

Sleep(100);

continue;

}

EnterCriticalSection(&cs);

cout << "philosopher " << num << " eatting" << endl;

LeaveCriticalSection(&cs);

Sleep(TIME_EATING);

break;

}

//设置时间为有信号

SetEvent(event[num]);

return 1;

}

int main(int argc, char* argv[])

{

HANDLE hThread;

InitializeCriticalSection(&cs);

//循环建立线程

for (int i = 0; i < PHILOSOPHERS; i++)

{

mutex[i] = CreateMutex(NULL, FALSE, "");

event[i] = CreateEvent(NULL, TRUE, FALSE, "");

hThread = (HANDLE)_beginthreadex(NULL, 0, ThreadFunc, (void*)i, 0, NULL);

if (hThread == 0)

{

cout << "create thread " << i << "failed with code: "

<< GetLastError() << endl;

DeleteCriticalSection(&cs);

return -1;

}

CloseHandle(hThread);

}

//等待所有的哲学家吃饭结束

DWORD ret = WaitForMultipleObjects(PHILOSOPHERS, event, TRUE, INFINITE);

if (ret == WAIT_OBJECT_0)

{

cout << "all the philosophers had a dinner!" << endl;

}

else

{

cout << "WaitForMultipleObjects failed with code: " << GetLastError() << endl;

}

DeleteCriticalSection(&cs);

for (int j = 0; j < PHILOSOPHERS; j++)

{

CloseHandle(mutex[j]);

}

return 1;

}

三 事件

事件对象的特点是它可以应用在重叠I/O(overlapped I/0)上,比如说socket编程中有两种模型,一种是重叠I/0,一种是完成端口都是可以使用事件同步。它也是核心对象,因此可以被waitforsingleobje这些函数等待;事件可以有名字,因此可以被其他进程开启。我在前几个例子当中其实已经使用到event了,在这里就不多说了,可以参考前一个例子。

四 信号量

semaphore的概念理解起来可能要比mutex还难, 我先简单说一下创建信号量的函数,因为我在开始使用的时候没有很快弄清楚,可能现在还有理解不对的地方,如果有错误还是请大侠多多指教。

CreateSemaphore(

LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, // SD

LONG lInitialCount, // initial count

LONG lMaximumCount, // maximum count

LPCTSTR lpName // object name

)

第一个参数是安全性,可以使用默认的安全性选项NULL;第二个和第三个参数是两个long型的数值,它们表示什么含义呢?lMaxinumCount表示信号量的最大值,必须要大于零。比如是5就表示可以有5个进程或者线程使用,如果第六个进程或者线程想使用的话就必须进入等待队列等待有进程或者线程释放资源。lInitalCount表示信号量的初始值,应该大于或者等于零小于等于lMaximumCount。如果lInitialCount = 0 && lMaximumCount == 5,那么就表示当前资源已经全部被使用,如果再有进程或者线程想使用的话,信号量就会变成-1,该进程或者线程进入等待队列,直到有进程或者线程执行ReleaseMutex;如果lInitialCount = 5 && lMaximumCount == 5,那么就表示现在信号量可以被进程或者线程使用5次,再之后就要进行等待;如果InitialCount = 2 && MaximumCount == 5这样的用法不太常见,表示还可以调用两次CreateSemaphore或者OpenSemaphore,再调用的话就要进入等待状态。最后一个参数表示这个信号量的名字,这样就可以跨进程的时候通过这个名字OpenSemaphore。说了这么多了,不知道说明白没有

看个例子,popo现在好像在本机只能运行三个实例,我们在前面说的mutex可以让程序只是运行一个实例,下面我通过信号量机制让程序像popo一样运行三个实例。

#include "stdafx.h"

#include <windows.h>

#include <iostream>

using namespace std;

const int MAX_RUNNUM = 3; //最多运行实例个数

void PrintInfo()

{

char c;

cout << "run program" << endl;

cout << "input s to exit program!" << endl;

while (1)

{

cin >> c;

if (c == 's')

{

break;

}

Sleep(10);

}

}

int main(int argc, char* argv[])

{

HANDLE hSe = CreateSemaphore(NULL, MAX_RUNNUM, MAX_RUNNUM, "semaphore_test");

DWORD ret = 0;

if (hSe == NULL)

{

cout << "createsemaphore failed with code: " << GetLastError() << endl;

return -1;

}

ret = WaitForSingleObject(hSe, 1000);

if (ret == WAIT_TIMEOUT)

{

cout << "you have runned " << MAX_RUNNUM << " program!" << endl;

ret = WaitForSingleObject(hSe, INFINITE);

}

PrintInfo();

ReleaseSemaphore(hSe, 1, NULL);

CloseHandle(hSe);

return 0;

}

附录:

核心对象

Change notification

Console input

Event

Job

Mutex

Process

Semaphore

Thread

Waitable timer

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有