linux带有超时等待的信号量,C++多线程同步之Semaphore(信号量)

一、线程间同步的几种方式

从上篇博文中可以发现,当多个线程对同一资源进行使用时,会产生“争夺”的情况,为了避免这种情况的产生,也就出现了线程间的同步这个技术。线程间的同步有多种方式,在接下来的博文中我会依次介绍几种主流的同步方式,以及他们之间的区别。在本篇博文中将介绍使用信号量Semaphore达到线程间同步的目的。老规矩,所有代码都讲在win32平台和Linux平台下都实现一遍。

相关函数和头文件

//头文件

#include

//创建信号量API

HANDLE WINAPI CreateSemaphore(

_In_opt_ LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,//指向SECURITY_ATTRIBUTES的指针;

_In_ LONG lInitialCount, //信号量对象的初始值;

_In_ LONG lMaximumCount, //信号量对象的最大值,这个值必须大于0;

_In_opt_ LPCTSTR lpName //信号量对象的名称;

);

//等待信号量API

DWORD WINAPI WaitForSingleObject(

_In_ HANDLE hHandle, //信号量对象句柄

_In_ DWORD dwMilliseconds //等待信号量时间,INFINET代表永久等待;

);

返回值:

WAIT_ABANDONED(0x00000080L) 表示拥有信号量的线程再终止前未释放该信号量;

WAIT_OBJECT_0(0x00000000L) 表示等到了信号量;

WAIT_TIMEOUT(0x00000102L) 表示等待超时;

WAIT_FAILED((DWORD)0xFFFFFFFF) 表示该函数执行失败,用GetLastError()得到错误码;

//释放信号量句柄

BOOL WINAPI ReleaseSemaphore(

_In_ HANDLE hSemaphore, //信号量对象句柄;

_In_ LONG lReleaseCount, //信号量释放的值,必须大于0;

_Out_opt_ LPLONG lpPreviousCount //前一次信号量值的指针,不需要可置为空;

);

返回值:成功返回非0;

Win32平台下源码

#include

#include

using namespace std;

HANDLE g_hSemaphore = NULL; //声明信号量变量

unsigned long WINAPI Fun(LPVOID lpParamter)

{

int iRunTime = 0;

//执行100次跳出

while(++iRunTime<100)

{

WaitForSingleObject(g_hSemaphore, INFINITE); //信号量值-1

cout << "Fun() is running!"<

ReleaseSemaphore(g_hSemaphore, 1, NULL); //信号量值+1

Sleep(10);

}

ExitThread(-1);

}

int main()

{

//创建信号量对象

g_hSemaphore = CreateSemaphore(NULL //信号量的安全特性

, 1 //设置信号量的初始计数。可设置零到最大值之间的一个值

, 1 //设置信号量的最大计数

, NULL //指定信号量对象的名称

);

if(NULL == g_hSemaphore)

{

cout << "create hSemaphore failed! error_code:"<

return 0;

}

int iRunTime = 0;

unsigned long ulThreadId = 0;

//创建一个子线程

HANDLE hThread = CreateThread(NULL, 0, Fun, NULL, 0, &ulThreadId);

//执行100次跳出

while(++iRunTime<100)

{

WaitForSingleObject(g_hSemaphore, INFINITE); //信号量值-1

cout << "main() is running, Thread id is " << ulThreadId <

ReleaseSemaphore(g_hSemaphore, 1, NULL); //信号量值+1

Sleep(10);

}

system("pause");

return 0;

}

执行结果:

00d8e376025823244b15f6cdde59b328.png 

可见未对屏幕资源产生“争夺”的情况,达到线程同步的目的。

Linux平台

相关函数和头文件

int sem_init(sem_t *sem, int pshared, unsigned int value);

1)pshared==0 用于同一多线程的同步;

2)若pshared>0 用于多个相关进程间的同步(即由fork产生的);

int sem_getvalue(sem_t *sem, int *sval);

取回信号量sem的当前值,把该值保存到sval中。

若有1个或更多的线程或进程调用sem_wait阻塞在该信号量上,该函数返回两种值:

1) 返回0

2) 返回阻塞在该信号量上的进程或线程数目

linux采用返回的第一种策略。

sem_wait(或sem_trywait)相当于P操作,即申请资源。

int sem_wait(sem_t *sem); // 这是一个阻塞的函数

测试所指定信号量的值,它的操作是原子的。

若sem>0,那么它减1并立即返回。

若sem==0,则睡眠直到sem>0,此时立即减1,然后返回;

int sem_trywait(sem_t *sem); // 非阻塞的函数

其他的行为和sem_wait一样,除了:

若sem==0,不是睡眠,而是返回一个错误EAGAIN。

sem_post相当于V操作,释放资源。

int sem_post(sem_t *sem);

把指定的信号量sem的值加1;

呼醒正在等待该信号量的任意线程。

源码

#include

#include

#include

using namespace std;

static sem_t g_semaphore;

static const int g_iRunTime = 5000;

void* Fun(void* ptr)

{

int iRunTime = 0;

while(++iRunTime< g_iRunTime)

{

sem_wait(&g_semaphore);

cout<< "Fun() is running!" << endl;

sem_post(&g_semaphore);

usleep(100);

}

}

int main()

{

pthread_t hHandle;

sem_init(&g_semaphore, 0, 1);

int iRet = pthread_create(&hHandle, NULL, Fun, NULL); //create a thread;

if(0 != iRet)

{

cout << "Create thread failed!" << endl;

}

sleep(1);

int iRunTime = 0;

while(++iRunTime

{

sem_wait(&g_semaphore);

cout << "main is running!" << endl;

sem_post(&g_semaphore);

usleep(100);

}

pthread_join(hHandle, NULL);

return 0;

}

执行结果

a1a25327c504bd52a4d7d57b40a00471.png 

达到同步效果!

关于Linux信号量

Linux信号量比Windows要复杂,上述例子只是使用了其中最常用的一种,还有许多其他种类的信号量,后期会补上一篇关于Linux信号量详解的内容。

线程间同步之 semaphore&lpar;信号量&rpar;