win32进阶必备:多线程同步之互斥锁

应用多线程互斥锁之前首先简单过一下C程序可能用到的3个创建线程函数:

CreateThread,windows系统提供的唯一创建线程API,_beginthread和_beginthreadex都在内部调用了CreateThread,直接调用该函数创建多线程的C程序存在内存泄露的可能性,通常不推荐直接使用,创建多线程应用程序时以_beginthreadex替代,详细原因下面讲解。

_beginthread,最初版的C运行时库多线程创建函数,参数过少,存在一些天然的缺陷,无法创建具有运行安全属性的线程,无法创建初始状态为挂起的线程,已被_beginthreadex全面替代,更加不推荐使用。

_beginthreadex ,正宗的多线程创建函数,能用_beginthreadex就不要用上面两个,相比CreateThread这个WIN32API,_beginthreadex额外做了线程局部存储空间的分配,C运行时一些函数在使用时会检测线程局部存储结构的指针,如果该指针为空就会自己创建一块内存用作局部存储,但要命的是这些函数并不负责释放这些局部存储空间,而CreateThread并不提供线程局部存储结构的指针,调用这些函数就会导致内存泄露,这些函数包括但不限于malloc、fopen、ctime、localtime。_beginthreadex会准备这样一个内存块,对应的_endthreadex会释放掉这个内存块。

网传很靠谱的_beginthreadex的工作过程:

1.创建C/C++运行期库所需的tiddat结构。

2.传递给_beginthreadex的线程启动函数也保存在tiddat结构中,传递给_beginthreadex的参数也保存在该结构中。

3._beginthreadex调用CreateThread函数创建线程时替换了线程启动函数和参数,线程启动函数替换为_threadstartex,线程启动传入参数替换为tiddat结构。

4.替换后的线程启动函数额外包含了C/C++运行时库所需的局部存储空间信息,同时也保留了CreateThread提供的所有安全属性,创建线程必选它啊。

先展示创建多个线程并使用互斥锁保证线程连续输出屏幕的一段代码,再根据代码讲解注释以外的互斥锁知识:

unsigned int
_stdcall ThreadExFunc(void* p)

{

//引用已存在的互斥锁核心对象,成功则核心对象引用计数+1

HANDLE hMutex
= OpenMutex(SYNCHRONIZE , TRUE, TEXT("foo46"));

if(hMutex ==
NULL)

printf("Wrong
Mutex");

else

{

//WaitforsingleObject将等待指定的一个mutex,直至获取到拥有权

//通过互斥锁保证除非输出工作全部完成,否则其他线程无法输出。

WaitForSingleObject(hMutex,
1000);

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

{

printf("%d%d%d%d%d%d%d%d%d%d\n",
p, p, p, p, p, p, p, p, p,p);

//休息10ms,大约为CPU的一个任务切换分片,即切换到下一个线程工作。

Sleep(10);

}

ReleaseMutex(hMutex);

}

//引用计数-1

CloseHandle(hMutex);

return 0;

}

void
foo46()

{

int i;

unsigned int
hTrd[5];

unsigned int
iThreadID;

BOOL bRc;

DWORD
ExitCode;

BOOL bRunning
= FALSE;

//CreateMutex后核心对象hMutex引用计数为1,当有其他线程OpenMutex或者CreateMutex时引用计数+1

//MUTEX的拥有权属于最后一个Wait并且尚未Release的线程

//MUTEX的摧毁和拥有权没有关系,当有一个线程对此调用CloseHandle时引用计数减1,引用计数为0时被摧毁。

HANDLE hMutex
= CreateMutex(NULL, FALSE, TEXT("foo46"));

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

{

//创建一个线程后,调用_beginthreadex会创建该线程核心对象,被创建的线程也会开启该核心对象

//线程创建后该核心对象引用计数为2

hTrd[i] =
_beginthreadex(NULL,

0,

ThreadExFunc,

(void*)i,

0,

&iThreadID);

//创建线程后即可调用CloseHandle关闭线程核心对象,调用CloseHandle只不过表示自己不想和这个线程对象有任何关系,调用后也没有真正的关闭

//线程核心对象,只是将线程核心对象的引用计数减一,如果引用计数变为0,该核心对象会被摧毁。

//线程核心对象并不指向线程本身

}

//等待所有工作线程都推出后才退出主线程

//进程启动后的第一个线程叫做主线程,主线程的结束会导致其他所有线程强制退出

//在窗口程序中主线程默认处理消息队列,通常将主线程设置为界面线程。

while(bRunning)

{

bRunning =
FALSE;

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

{

bRc =
GetExitCodeThread((HANDLE)hTrd[i], &ExitCode);

if(bRc
&& ExitCode == STILL_ACTIVE)

bRunning =
TRUE;

}

}

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

{

CloseHandle((HANDLE)hTrd[i]);

}

}

互斥锁的使用通常有以下API:

CreateMutex,创建一个互斥锁核心对象,如果创建的核心对象已存在,会返回句柄,如果已经存在调用GetLastError会返回ERROR_ALREADY_EXITS。

OpenMutex,打开一个已经存在互斥锁核心对象,通常用于客户端获取服务器的互斥锁对象。

WaitForSingleObject,尝试获取一个内核核心对象的拥有权,可以设置等待时间,有三种情况会返回:1.成功获取拥有权;2.等待超时返回;3.等待中的互斥锁对象被废弃,即线程Wait到MUTEX后尚未调用ReleaseMutex就异常退出,通过WaitForSingleObject的返回值可以判断。

WaitForMultiPleObjects,尝试获取多个内核核心对象的拥有权,可以设置互斥锁对象列表、是否等待所有、等待超时时间。使用和WaitForSingleObject不是很一样,详细参见MSDN。

MsgWaitForMultipleObjects,WaitForMultipleObjects只有在对象被激发或核心对象废弃再或者等待超时才会返回,而主界面窗口还要等待消息,如果有消息到来也要立即返回并处理消息,MsgWaitForMultipleObjects就是这样一个同时等待消息和对象激发信号的函数。

ReleaseMutex,工作完成后调用,释放互斥锁拥有权。

CloseHandle,告诉操作系统不再需要引用该内核对象,请将引用计数-1.

小结:互斥锁的基础知识大概就这么多了,应用时要麻烦的多,就如侯捷所说:知道哪一个mutex被舍弃是一件简单的事,想要正确处理却不容易,mutex是用来保护操作自动进行的,如果线程死于半途,很有可能被保护的数据就会受到不可修复的伤害。

时间: 2024-10-12 15:25:02

win32进阶必备:多线程同步之互斥锁的相关文章

多线程同步之互斥对象

多线程同步之互斥对象 作者:vpoet mail:[email protected] 在http://blog.csdn.net/u013018721/article/details/46637215一文中介绍了使用临界区 对卖票问题进行线程间同步,本文将在上文的基础上,使用互斥对象对线程进行同步. 首先看看windows API 该函数创建一个命名或者不命名的互斥对象 lpMutexAttributes [in] Pointer to a SECURITY_ATTRIBUTES structu

线程同步(互斥锁与信号量的作用与区别)

“信号量用在多线程多任务同步的,一个线程完成了某一个动作就通过信号量告诉别的线程,别的线程再进行某些动作(大家都在semtake的时候,就阻塞在 哪里).而互斥锁是用在多线程多任务互斥的,一个线程占用了某一个资源,那么别的线程就无法访问,直到这个线程unlock,其他的线程才开始可以利用这 个资源.比如对全局变量的访问,有时要加锁,操作完了,在解锁.有的时候锁和信号量会同时使用的” 也就是说,信号量不一定是锁定某一个资源,而是流程上的概念,比如:有A,B两个线程,B线程要等A线程完成某一任务以后

Linux 多线程同步机制:互斥量、信号量、条件变量

互斥量:互斥量提供对共享资源的保护访问,它的两种状态:lock和unlock,用来保证某段时间内只有一个线程使用共享资源,互斥量的数据类型是pthread_mutex_t主要涉及函数:pthread_mutex_lock() pthread_mutex_trylock() pthread_mutex_unlock()Pthreaf_mutex_init() pthread_mutex_destroy()lock与unlock之间所锁定的区域为临界区域(如果只加锁不解锁程序会阻塞等待)信号量:信号

APUE学习笔记——11 线程同步、互斥锁、自旋锁、条件变量

线程同步 同属于一个进程的不同线程是共享内存的,因而在执行过程中需要考虑数据的一致性. 假设:进程有一变量i=0,线程A执行i++,线程B执行i++,那么最终i的取值是多少呢?似乎一定是i=2:其实不然,如果没有考虑线程同步,i的取值可能是1.我们先考虑自加操作的过程:a,首先将内存中i的值copy到寄存器:b,对寄存器中i的copy进行自加:c,将寄存器中自加的结果返回到内存中.回到例子,如果线程A执行完abc三个步骤,线程B在执行者三个步骤,那么结果就应该为2.但是自加不是原子操作,假如执行

Linux下多线程编程之互斥锁、条件变量、信号量

1.进程创建 int pthread_create (pthread_t * thread_id, __const pthread_attr_t * __attr, void *(*__start_routine) (void *), void *__restrict __arg); 第一个参数为指向线程标识符的指针,第二个参数用来设置线程属性,第三个参数是线程运行函数的起始地址,最后一个参数是运行函数的参数. 一个实例: void *producer(void *args); pthread_

Linux多线程同步之互斥量和条件变量

1. 什么是互斥量 互斥量从本质上说是一把锁,在访问共享资源前对互斥量进行加锁,在访问完成后释放互斥量上的锁.对互斥量进行加锁以后,任何其他试图再次对互斥量加锁的线程将会被阻塞直到当前线程释放该互斥锁.如果释放互斥锁时有多个线程阻塞,所以在该互斥锁上的阻塞线程都会变成可进行状态,第一个变成运行状态的线程可以对互斥量加锁,其他线程在次被阻塞,等待下次运行状态. pthread_mutex_t 就是POSIX对于mutex的实现. 函数名 参数 说明 pthread_mutex_init pthre

生产者消费者-(多线程同步和互斥)

条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个线程等待条件变量的条件成立而挂起(此时不再占用cpu):另一个线程使条件成立(给出条件成立信号).为了防止竞争,条件变量的使用总是和一个互斥锁结合在一起. /* 等待条件 */ /* 注意:pthread_cond_wait为阻塞函数.解开锁,再等待.等条件满足时,需要抢到锁,才可以被唤醒*/ pthread_cond_wait(&cond_pro,&mutex); /* 激发条件 */ /* 所有因为不满足条件的

Linux-线程同步之互斥锁

1.互斥锁又叫互斥量(mutex) 2.相关函数:pthread_mutex_init  pthread_mutex_destroy   pthread_mutex_lock pthread_mutex_unlock 3.互斥锁与信号量的关系:可以认为互斥锁是一种特殊的信号量 4.互斥锁主要用来实现关键段的保护 提示:man  3 pthread_mutex_init时提示找不到函数,说明你没有安装pthread相关的man手册. 安装方法:1.虚拟机上网: 2.sudo   apt-get  

Java中多线程如何使用互斥锁实现资源共享

假设这样一个情景:在银行的营业厅内先后进来3个人,他们都要进行存款,若是只有一个营业窗口的话,通常的情况是每人都需要先领取顺序条,然后按序排队办理业务,而营业厅会根据号码的顺序依次叫号来处理顾客的问题. 在这里银行的窗口就可以看做共享的资源,它每次只能接待一个顾客,而不同的顾客则可以看做是多个线程,他们都需要办理业务,但是又必须遵守先来后到的原则,排队等待前面的顾客办理完业务才能轮到自己独占窗口办理自己的业务(当然也可能存在插队现象,后面会讲到). 为了简化银行处理业务的过程,我们假设每个客户只