应用多线程互斥锁之前首先简单过一下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是用来保护操作自动进行的,如果线程死于半途,很有可能被保护的数据就会受到不可修复的伤害。

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

  1. 线程同步 - POSIX互斥锁

    线程同步 - POSIX互斥锁 概括 本文讲解POSIX中互斥量的基本用法,从而能达到简单的线程同步.互斥量是一种特殊的变量,它有两种状态:锁定以及解锁.如果互斥量是锁定的,就有一个特定的线程持有或者 ...

  2. python同步、互斥锁、死锁

    目录 同步 同步的概念 解决线程同时修改全局变量的方式 互斥锁 使用互斥锁完成2个线程对同一个全局变量各加9999999 次的操作 上锁解锁过程 总结 死锁 避免死锁 同步 同步的概念 同步就是协同步 ...

  3. 【多线程】C++ 互斥锁(mutex)的简单原理分析

    多线程是多任务处理的一种特殊形式,多任务处理允许让电脑同时运行两个或两个以上的程序.一般情况下,分为两种类型的多任务处理:基于进程和基于线程. 1)基于进程的多任务处理是程序的并发执行. 2)基于线程 ...

  4. c#中多线程同步Lock(锁)的研究以及跨线程UI的操作

    本文只针对C#中,多线程同步所用到的锁(lock)作为研究对象.由于想更直观的显示结果,所以,在做demo的时候,就把多线程通过事件操作UI的代码也写了出来,留作备忘和分享吧. 其实多线程的同步,使用 ...

  5. c# 多线程 --Mutex(互斥锁)

    互斥锁(Mutex) 互斥锁是一个互斥的同步对象,意味着同一时间有且仅有一个线程可以获取它. 互斥锁可适用于一个共享资源每次只能被一个线程访问的情况 函数: //创建一个处于未获取状态的互斥锁 Pub ...

  6. linux多线程编程之互斥锁

    多线程并行运行,共享同一种互斥资源时,需要上互斥锁来运行,主要是用到pthread_mutex_lock函数和pthread_mutex_unlock函数对线程进行上锁和解锁 下面是一个例子: #in ...

  7. UNIX环境高级编程——线程同步之互斥锁、读写锁和条件变量(小结)

    一.使用互斥锁 1.初始化互斥量 pthread_mutex_t mutex =PTHREAD_MUTEX_INITIALIZER;//静态初始化互斥量 int pthread_mutex_init( ...

  8. c# 多线程 --Mutex(互斥锁) 【转】

    互斥锁(Mutex) 互斥锁是一个互斥的同步对象,意味着同一时间有且仅有一个线程可以获取它. 互斥锁可适用于一个共享资源每次只能被一个线程访问的情况 函数: //创建一个处于未获取状态的互斥锁 Pub ...

  9. python 多线程 及多线程通信,互斥锁,线程池

    1.简单的多线程例子 import threading,timedef b_fun(i): print "____________b_fun start" time.sleep(7 ...

随机推荐

  1. 《 UNIX网络编程》源码的使用

    学习编程这东西,看代码,改代码,运行代码这样才能学到实际东西!本书说在www.unpbook.com可以获取源码,不过打不开!所以google unpv13e.tar.gz 并在网络上找到了:源码:h ...

  2. 体验Azure的 Automation “自动化” 服务预览版

    一直想要实现对Azure上跑的虚机进行定期的自动备份.在网上搜了一下有关的解决方案,发现一个不错的帖子,是基于国外的Azure服务做的.基本原理就是利用Azure的Automation(自动化)服务来 ...

  3. ORACLE SQLloader详细语法

    Oracle   SQL   Loader的详细语法     SQL*LOADER是ORACLE的数据加载工具,通常用来将操作系统文件迁移到ORACLE数据库中.SQL*LOADER是大型数据     ...

  4. 【转】google play上传应用

    原文网址:https://support.google.com/googleplay/android-developer/answer/113469?hl=zh-Hans 注册 Google Play ...

  5. 【转】java提高篇(十)-----详解匿名内部类

    原文网址:http://www.cnblogs.com/chenssy/p/3390871.html 在java提高篇-----详解内部类中对匿名内部类做了一个简单的介绍,但是内部类还存在很多其他细节 ...

  6. 【转】 当程序崩溃的时候怎么办 Part-2

    转自:http://www.tairan.com/archives/1143 欢迎回到当程序崩溃的时候怎么办 教程! 在这个教程的第一部分,我们介绍了SIGABRT和EXC_BAD_ACCESS错误, ...

  7. HDU4614 Vases and Flowers 二分+线段树

    分析:感觉一看就是二分+线段树,没啥好想的,唯一注意,当开始摆花时,注意和最多能放的比大小 #include<iostream> #include<cmath> #includ ...

  8. HDU 4777 Rabbit Kingdom 树状数组

    分析:找到每一个点的左边离他最近的不互质数,记录下标(L数组),右边一样如此(R数组),预处理 这个过程需要分解质因数O(n*sqrt(n)) 然后离线,按照区间右端点排序 然后扫一遍,对于当前拍好顺 ...

  9. Can't find file: './mysql/plugin.frm' (errno: 13)[mysql数据目录迁移错位]错误解决

    大概需要4个步骤,其中第1步通过service mysql stop停止数据库,第4步通过service mysql start启动数据库. 第2步移动数据文件,不知道是否为Ubuntu智能的原因,移 ...

  10. Webdriver API (一)

    (转载) 1.1  下载selenium2.0的包 官方download包地址:http://code.google.com/p/selenium/downloads/list 官方User Guid ...