<!-- LANGUAGE='JavaScript'>write_body();<-->
<!-- LANGUAGE='JavaScript'>write_bar();<-->
<table width=98% cellspacing="0" cellpadding="0" align=center><!--整体框架-->
<tr><td>
<table border=0 width="100%" cellspacing="0" cellpadding="2"><!--标记放置区域-->
<tr>
<td width="30%" align="center" bgcolor="#8E8E8E" valign=middle><img src=../../img/brand_200_60.gif width=200 height=60 alt="LOGO1"></td>
<td width="70%" align="center" bgcolor="#8E8E8E" valign=middle><!-- LANGUAGE='JavaScript'>write_ban();<--></td>
</tr>
<tr>
<td colspan="2" bgcolor="#939393" align=center><font color=white>您当前位置</font> <a href=../../index.htm><font color=white>首页</font></a> <a href=../index.htm><font color=white>开发教程</font></a> <a href=index.htm><font color=white><font class=engul>Visual C++/MFC</font>专题讲座</font></a> <font color=white>4.4 进程/线程间同步</font> <font color=white><!-- LANGUAGE='JavaScript'>write_command();<--></font></td>
</tr>
</table><!--标记放置区域 END-->
<table border=0 width=100% cellspacing="0" cellpadding="0">
<tr bgcolor="#F4F4F4">
<td><!-- article title begin here-->
<br>
<p align=center><big>4.4 进程/线程间同步</big></p>
<p>这一节的内容比较多请你耐心的看完,因为进程/线程间同步的方法比较多,每种方法都有不同的用途:这节中会讲通过<a href=#critical_section>临界区</a>,<a href=#mutex>互斥量</a>,<a href=#semaphore>信号灯</a>,<a href=#event>事件</a>来进行同步。
<p>由于进程/线程间的操作是并行进行的,所以就产生了一个数据的问题同步,我们先看一段代码:
<pre>
int iCounter=0;//全局变量
DOWRD threadA(void* pD)
{
for(int i=0;i<100;i++)
{
int iCopy=iCounter;
//Sleep(1000);
iCopy++;
//Sleep(1000);
iCounter=iCopy;
}
}
</pre>
现在假设有两个线程threadA1和threadA2在同时运行那么运行结束后iCounter的值会是多少,是200吗?不是的,如果我们将Sleep(1000)前的注释去掉后我们会很容易明白这个问题,因为在iCounter的值被正确修改前它可能已经被其他的线程修改了。这个例子是一个将机器代码操作放大的例子,因为在CPU内部也会经历数据读/写的过程,而在线程执行的过程中线程可能被中断而让其他线程执行。<font color=red>变量iCounter在被第一个线程修改后,写回内存前如果它又被第二个线程读取,然后才被第一个线程写回,那么第二个线程读取的其实是错误的数据,这种情况就称为脏读(dirty read)。</font>这个例子同样可以推广到对文件,资源的使用上。
<p>那么要如何才能避免这一问题呢,假设我们在使用iCounter前向其他线程询问一下:有谁在用吗?如果没被使用则可以立即对该变量进行操作,否则等其他线程使用完后再使用,而且在自己得到该变量的控制权后其他线程将不能使用这一变量,直到自己也使用完并释放为止。经过修改的伪代码如下:
<pre>
int iCounter=0;//全局变量
DOWRD threadA(void* pD)
{
for(int i=0;i<100;i++)
{
ask to lock iCounter
wait other thread release the lock
lock successful
{
int iCopy=iCounter;
//Sleep(1000);
iCopy++;
}
iCounter=iCopy;
release lock of iCounter
}
}
</pre>
<p>幸运的是OS提供了多种同步对象供我们使用,并且可以替我们管理同步对象的加锁和解锁。我们需要做的就是对每个需要同步使用的资源产生一个同步对象,在使用该资源前申请加锁,在使用完成后解锁。接下来我们介绍一些同步对象:
<a name=critical_section></a><p>临界区:临界区是一种最简单的同步对象,它只可以在同一进程内部使用。它的作用是保证只有一个线程可以申请到该对象,例如上面的例子我们就可以使用临界区来进行同步处理。几个相关的API函数为:
<ul>
<li>VOID InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection );产生临界区
</li>
<li>VOID DeleteCriticalSection(LPCRITICAL_SECTION lpCriticalSection );删除临界区
</li>
<li>VOID EnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection );进入临界区,相当于申请加锁,如果该临界区正被其他线程使用则该函数会等待到其他线程释放
</li>
<li>BOOL TryEnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection );进入临界区,相当于申请加锁,和EnterCriticalSection不同如果该临界区正被其他线程使用则该函数会立即返回FALSE,而不会等待
</li>
<li>VOID LeaveCriticalSection(LPCRITICAL_SECTION lpCriticalSection );退出临界区,相当于申请解锁
</li>
</ul>
<p>下面的示范代码演示了如何使用临界区来进行数据同步处理:
<pre>
//全局变量
int iCounter=0;
CRITICAL_SECTION criCounter;
DWORD threadA(void* pD)
{
int iID=(int)pD;
for(int i=0;i<8;i++)
{
EnterCriticalSection(&criCounter);
int iCopy=iCounter;
Sleep(100);
iCounter=iCopy+1;
printf("thread %d : %d\n",iID,iCounter);
LeaveCriticalSection(&criCounter);
}
return 0;
}
//in main function
{
//创建临界区
InitializeCriticalSection(&criCounter);
//创建线程
HANDLE hThread[3];
CWinThread* pT1=AfxBeginThread((AFX_THREADPROC)threadA,(void*)1);
CWinThread* pT2=AfxBeginThread((AFX_THREADPROC)threadA,(void*)2);
CWinThread* pT3=AfxBeginThread((AFX_THREADPROC)threadA,(void*)3);
hThread[0]=pT1->m_hThread;
hThread[1]=pT2->m_hThread;
hThread[2]=pT3->m_hThread;
//等待线程结束
//至于WaitForMultipleObjects的用法后面会讲到。
WaitForMultipleObjects(3,hThread,TRUE,INFINITE);
//删除临界区
DeleteCriticalSection(&criCounter);
printf("\nover\n");
}
<a name=mutex></a></pre>
<p>接下来要讲互斥量与临界区的作用非常相似,但互斥量是可以命名的,也就是说它可以跨越进程使用。所以创建互斥量需要的资源更多,所以如果只为了在进程内部是用的话使用临界区会带来速度上的优势并能够减少资源占用量。因为互斥量是跨进程的互斥量一旦被创建,就可以通过名字打开它。下面介绍可以用在互斥量上的API函数:
<pre>创建互斥量:
HANDLE CreateMutex(
LPSECURITY_ATTRIBUTES lpMutexAttributes,// 安全信息
BOOL bInitialOwner, // 最初状态,
//如果设置为真,则表示创建它的线程直接拥有了该互斥量,而不需要再申请
LPCTSTR lpName // 名字,可以为NULL,但这样一来就不能被其他线程/进程打开
);
打开一个存在的互斥量:
HANDLE OpenMutex(
DWORD dwDesiredAccess, // 存取方式
BOOL bInheritHandle, // 是否可以被继承
LPCTSTR lpName // 名字
);
释放互斥量的使用权,但要求调用该函数的线程拥有该互斥量的使用权:
BOOL ReleaseMutex(//作用如同LeaveCriticalSection
HANDLE hMutex // 句柄
);
关闭互斥量:
BOOL CloseHandle(
HANDLE hObject // 句柄
);
<a name=waitfor></a></pre>
<p><font color=red>你会说为什么没有名称如同EnterMutex,功能如同EnterCriticalSection一样的函数来获得互斥量的使用权呢?的确没有!</font>获取互斥量的使用权需要使用函数:
<pre>
DWORD WaitForSingleObject(
HANDLE hHandle, // 等待的对象的句柄
DWORD dwMilliseconds // 等待的时间,以ms为单位,如果为INFINITE表示无限期的等待
);
返回:
WAIT_ABANDONED 在等待的对象为互斥量时表明因为互斥量被关闭而变为有信号状态
WAIT_OBJECT_0 得到使用权
WAIT_TIMEOUT 超过(dwMilliseconds)规定时间
</pre>
<p>在线程调用WaitForSingleObject后,如果一直无法得到控制权线程讲被挂起,直到超过时间或是获得控制权。
<p>讲到这里我们必须更深入的讲一下WaitForSingleObject函数中的对象(Object)的含义,这里的对象是一个具有信号状态的对象,对象有两种状态:有信号/无信号。而等待的含义就在于等待对象变为有信号的状态,对于互斥量来讲如果正在被使用则为无信号状态,被释放后变为有信号状态。当等待成功后WaitForSingleObject函数会将互斥量置为无信号状态,这样其他的线程就不能获得使用权而需要继续等待。WaitForSingleObject函数还进行排队功能,保证先提出等待请求的线程先获得对象的使用权,下面的代码演示了如何使用互斥量来进行同步,代码的功能还是进行全局变量递增,通过输出结果可以看出,先提出请求的线程先获得了控制权:
<pre>
int iCounter=0;
DWORD threadA(void* pD)
{
int iID=(int)pD;
//在内部重新打开
HANDLE hCounterIn=OpenMutex(MUTEX_ALL_ACCESS,FALSE,"sam sp 44");
for(int i=0;i<8;i++)
{
printf("%d wait for object\n",iID);
WaitForSingleObject(hCounterIn,INFINITE);
int iCopy=iCounter;
Sleep(100);
iCounter=iCopy+1;
printf("\t\tthread %d : %d\n",iID,iCounter);
ReleaseMutex(hCounterIn);
}
CloseHandle(hCounterIn);
return 0;
}
//in main function
{
//创建互斥量
HANDLE hCounter=NULL;
if( (hCounter=OpenMutex(MUTEX_ALL_ACCESS,FALSE,"sam sp 44"))==NULL)
{
//如果没有其他进程创建这个互斥量,则重新创建
hCounter = CreateMutex(NULL,FALSE,"sam sp 44");
}
//创建线程
HANDLE hThread[3];
CWinThread* pT1=AfxBeginThread((AFX_THREADPROC)threadA,(void*)1);
CWin
没有合适的资源?快使用搜索试试~ 我知道了~
vc 书 Internet相关开发, ActiveX控件开发,调试技术与异常(错误),文件操作, 内存管理
共74个文件
gif:28个
htm:25个
zip:21个
4星 · 超过85%的资源 需积分: 0 80 下载量 5 浏览量
2009-11-09
16:34:59
上传
评论
收藏 937KB ZIP 举报
温馨提示
第一章 Internet相关开发 打包下载 |------ 1.1 如何编写CGI程序 |------ 1.2 一种更亲切的CGI开发系统WinCGI |------ 1.3 利用ISAPI开发CGI程序 |------ 1.4 利用WinInet开发Internet程序 +-- 第二章 ActiveX控件开发 打包下载 |------ 2.1 ActiveX控件介绍 |------ 2.2 利用MFC开发ActiveX控件 |------ 2.3 利用ATL(ActiveX模板库)创建ActiveX控件 |------ 2.4 调试并使用ActiveX控件 +-- 第三章 调试技术与异常(错误)处理 打包下载 |------ 3.1 跟踪与中间过程输出 |------ 3.2 变量/对象合法性检查 |------ 3.3 内存泄露检查 |------ 3.4 异常捕捉与处理 +-- 第四章 进程/线程控制 打包下载 |------ 4.1 为什么需要多进程/线程 |------ 4.2 进程控制 |------ 4.3 线程控制 |------ 4.4 进程/线程间同步 +------ 4.5 进程间通信 +-- 第五章 文件操作 打包下载 +------ 5.1 磁盘文件的打开与关闭 +------ 5.2 磁盘文件的正常读写与异步读写 +------ 5.3 磁盘文件的查找 +------ 5.4 磁盘文件的其他操作 +------ 5.5 串口的操作 +-- 第六章 内存管理 打包下载 +------ 6.1 Win32下的内存管理介绍 +------ 6.2 虚存的使用
资源推荐
资源详情
资源评论
收起资源包目录
vc相关书籍.zip (74个子文件)
teach_sp
teach_sp_31.htm 6KB
sp_teach_223.gif 10KB
teach_sp_6_pack.zip 31KB
sp_teach_341.gif 3KB
sam_sp_45.zip 24KB
teach_sp_45.htm 10KB
teach_sp_44.htm 21KB
teach_sp_2_pack.zip 141KB
sam_sp_62.zip 28KB
sp_teach_234.gif 6KB
teach_sp_21.htm 3KB
sp_teach_227.gif 4KB
sam_sp_53.zip 6KB
teach_sp_11.htm 8KB
teach_sp_4_pack.zip 92KB
sp_teach_226.gif 9KB
sp_teach_311.gif 9KB
sam_sp_teach_23.zip 16KB
sam_sp_44.zip 25KB
sp_teach_312.gif 12KB
sp_teach_321.gif 7KB
teach_sp_22.htm 6KB
teach_sp_61.htm 5KB
teach_sp_24.htm 4KB
teach_sp_55.htm 7KB
teach_sp_32.htm 6KB
sp_teach_242.gif 7KB
sam_sp_33.zip 6KB
sam_sp_34.zip 11KB
sp_teach_232.gif 5KB
sp_teach_141.gif 7KB
sp_teach_621.gif 5KB
teach_sp_62.htm 6KB
teach_sp_3_pack.zip 89KB
sam_sp_31.zip 13KB
teach_sp_53.htm 7KB
teach_sp_5_pack.zip 57KB
sp_teach_243.gif 7KB
sp_teach_225.gif 6KB
teach_sp_13.htm 8KB
sp_teach_245.gif 7KB
teach_sp_54.htm 3KB
sam_sp_42.zip 10KB
sp_teach_222.gif 10KB
teach_sp_14.htm 5KB
sp_teach_246.gif 5KB
teach_sp_43.htm 6KB
sp_teach_411.gif 5KB
sp_teach_412.gif 3KB
sp_teach_441.gif 2KB
teach_sp_42.htm 7KB
teach_sp_41.htm 6KB
sp_teach_224.gif 6KB
sam_sp_teach_141.zip 11KB
sp_teach_244.gif 5KB
sam_sp_52_1.zip 9KB
sam_sp_52_2.zip 8KB
index.htm 6KB
sp_teach_231.gif 5KB
sp_teach_313.gif 16KB
teach_sp_1_pack.zip 35KB
teach_sp_33.htm 4KB
sp_teach_221.gif 11KB
teach_sp_51.htm 7KB
sam_sp_43.zip 10KB
sp_teach_241.gif 5KB
sam_sp_55.zip 19KB
teach_sp_12.htm 5KB
g_11_1.gif 2KB
teach_sp_52.htm 19KB
sp_teach_233.gif 3KB
sam_sp_teach_221.zip 24KB
teach_sp_34.htm 7KB
teach_sp_23.htm 4KB
共 74 条
- 1
yyyzlf
- 粉丝: 60
- 资源: 26
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
最新资源
- JavaScript《基于自动分析数据并给出营业建议的餐厅管理系统(接入AI) 》+源代码+项目说明及资料
- 355670834783295707ad04e-427f-4cde-9589-e578224a8459.zip
- 动态sql解析引擎,类似mybatis动态sql的功能
- EDA365-Skill-V2.5安装包,支持Allegro17.x版本
- C# 常用单词汇总,常用单词汇总
- 【ERP标准流程-标准流程-库内业务管理】(DOC 14页).doc
- Python《数据库期末作业-餐厅点单系统 》+源代码+设计资料
- 学生成绩管理系统(C++课程设计
- 双指针法判断链表有环-go语言实现
- MyBatis动态SQL是一种强大的特性,它允许我们在SQL语句中根据条件动态地添加或删除某些部分,从而实现更加灵活和高效的数据
资源上传下载、课程学习等过程中有任何疑问或建议,欢迎提出宝贵意见哦~我们会及时处理!
点击此处反馈
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功
- 1
- 2
前往页