首页 | 源码下载 | 编程控件 | 书籍教程 | 应用方案 | 设计素材 | 项目交易 | 开发文档 | 商业源码 | 我的帐号
登陆我的帐号
帐 号:
密 码:
我还不是会员,需要注册!

截止2004年12月16日
本站源码总量(商业源码除外)RAR压缩为 4,206,733 KB。其中免费源码为 1,124,495 KB,会员源码为 3,082,238 KB
C/C++ 129,555 KB
Delphi 1,258,381 KB
Java 120,937 KB
.Net 36,886 KB
PowerBuilder 954,525 KB
Visual Basic 923,454 KB
ASP 259,795 KB
JSP 4,987 KB
其他 94,723 KB

本站是中国频道、中资源、时代互联顶级代理:注册国际域名70元,国内域名130元,各类ASP、PHP、JSP空间8折优惠!
本站承担各类网站制作开发及方案策划,项目经验丰富,欢迎洽谈!

网站动态
网站春节前夕再次改版!
现在下载速度大幅提高!
想免费下载源码吗?
还有众多资源恭候大家免费…
道歉!
关于资源更新的说明
关于下载错误的原因!
源码资源网新版网站投入运…

当前位置:源码资源网首页 > 开发文档首页 > C/C++ >线程的基础知识

线程的基础知识
人气:36 文字大小:     作者:


5. 如何使用关键代码段实现线程的同步?
如果需要一小段代码以原子操作的方式执行,这时简单的互锁函数已不能满足需要,必须使用关键代码段来解决问题。不过使用关键代码段时,很容易陷入死锁状态,因为在等待进入关键代码段时无法设定超时值。关键代码段是通过对共享资源设置一个标志来实现的,就像厕所门上的“有人/没人”标志一样。这个标志就是一个CRITICAL_SECTION变量。该变量在任何一个线程使用它之前应当进行初始化。初始化可以有两种方法,使用InitializeCriticalSection函数和InitializeCriticalSectionAndSpinCount函数。然后在每个使用共享资源的线程函数的关键代码段前使用EnterCriticalSection函数或者使用TryEnterCriticalSection函数。在关键代码段使用之后调用LeaveCriticalSection函数。在所有的线程都不再使用该共享资源后应当调用DeleteCriticalSection函数来清除该标志。举例说明:
const int MAX_TIMES = 1000;
int g_intIndex = 0;
DWORD g_dwTimes[MAX_TIMES];
CRITICAL_SECTION g_cs;


void Init  )
{
……
InitializeCriticalSection  &g_cs );
……
}


DWORD WINAPI FirstThread  PVOID lpParam )
{
while   g_intIndex < MAX_TIMES )
{
EnterCriticalSection  &g_cs );
g_dwTimes[g_intIndex] = GetTickCount  );
g_intIndex++;
LeaveCriticalSection  &g_cs );
}
return 0;
}


DWORD WINAPI SecondThread  PVOID lpParam )
{
while   g_intIndex < MAX_TIMES )
{
EnterCriticalSection  &g_cs );
g_intIndex++;
g_dwTimes[g_intIndex - 1] = GetTickCount  );
LeaveCriticalSection  &g_cs );
}
return 0;
}


void Close  )
{
……
DeleteCriticalSection  &g_cs );
……
}
使用关键代码段应当注意一些技巧:
(1) 每个共享资源使用一个CRITICAL_SECTION变量。
这样在当前线程占有一个资源时,另一个资源可以被其他线程占有。
EnterCriticalSection  &g_cs );
for   intLoop = 0; intLoop < 100; intLoop++ )
{
g_intArray[intLoop] = 0;
g_uintArray[intLoop] = 0;
}
LeaveCriticalSection  &g_cs );
改为:
EnterCriticalSection  &g_csInt );
for   intLoop = 0; intLoop < 100; intLoop++ )
{
g_intArray[intLoop] = 0;
}
LeaveCriticalSection  &g_csInt );
EnterCriticalSection  &g_csUint );
for   intLoop = 0; intLoop < 100; intLoop++ )
{
g_uintArray[intLoop] = 0;
}
LeaveCriticalSection  &g_csUint );
(2) 同时访问多个资源,必须始终按照完全相同的顺序请求对资源的访问。
这样才能避免死锁状态产生。离开的顺序没有关系。
Thread1:
EnterCriticalSection  &g_csInt );
EnterCriticalSection  &g_csUint );
for   intLoop = 0; intLoop < 100; intLoop++ )
{
g_uintArray[intLoop] = g_intArray[intLoop];
}
LeaveCriticalSection  &g_csInt );
LeaveCriticalSection  &g_csUint );
Thread2:
EnterCriticalSection  &g_csUint );
EnterCriticalSection  &g_csInt );
for   intLoop = 0; intLoop < 100; intLoop++ )
{
g_uintArray[intLoop] = g_intArray[intLoop];
}
LeaveCriticalSection  &g_csInt );
LeaveCriticalSection  &g_csUint );
改为:
Thread1:
EnterCriticalSection  &g_csInt );
EnterCriticalSection  &g_csUint );
for   intLoop = 0; intLoop < 100; intLoop++ )
{
g_uintArray[intLoop] = g_intArray[intLoop];
}
LeaveCriticalSection  &g_csInt );
LeaveCriticalSection  &g_csUint );
Thread2:
EnterCriticalSection  &g_csInt );
EnterCriticalSection  &g_csUint );
for   intLoop = 0; intLoop < 100; intLoop++ )
{
g_uintArray[intLoop] = g_intArray[intLoop];
}
LeaveCriticalSection  &g_csInt );
LeaveCriticalSection  &g_csUint );
(3) 不要长时间运行关键代码段。
EnterCriticalSection  &g_cs );
SendMessage  hWnd, WM_SOMEMSG, &g_s, 0 );
LeaveCriticalSection  &g_cs );
改为:
EnterCriticalSection  &g_cs );
sTemp = g_s;
LeaveCriticalSection  &g_cs );
SendMessage  hWnd, WM_SOMEMSG, &sTemp, 0 );


6. InitializeCriticalSection/InitializeCriticalSectionAndSpinCount差别?
InitializeCriticalSection函数的返回值为空并且不会创建事件内核对象,比较节省系统资源,但是一旦发生两个或多个线程争用关键代码段的情况,如果内存不足,关键代码段可能被争用,同时系统可能无法创建必要的事件内核对象。这时EnterCriticalSection函数将会产生一个EXCEPTION_INVALID_HANDLE异常。这个错误非常少见。如果想对这种情况有所准备,可以有两种选择。可以使用结构化异常处理方法来跟踪错误。当错误发生时,既可以不访问关键代码段保护的资源,也可以等待某些内存变成可用状态,然后再次调用EnterCriticalSection函数。
另一种选择是使用InitializeCriticalSectionAndSpinCount,第二个参数dwSpinCount中,传递的是在使线程等待之前它试图获得资源时想要循环锁循环迭代的次数。这个值可以是0至0x00FFFFFF之间的任何数字。如果在单处理器计算机上运行时调用该函数,该参数被忽略,并且始终设置为0。使用InitializeCriticalSectionAndSpinCount函数创建关键代码段,确保设置了dwSpinCount参数的高信息位。当该函数发现高信息位已经设置时,它就创建该事件内核对象,并在初始化时将它与关键代码段关联起来。如果事件无法创建,该函数返回FALSE。可以更加妥善地处理代码中的这个事件。如果事件创建成功,EnterCriticalSection将始终都能运行,并且决不会产生异常情况(如果总是预先分配事件内核对象,就会浪费系统资源。只有当代码不能容许EnterCriticalSection运行失败,或者有把握会出现争用现象,或者预计进程将在内存非常短缺的环境中运行时,才能预先分配事件内核对象)。


7. TryEnterCriticalSection和EnterCriticalSection的差别是什么?
如果EnterCriticalSection将一个线程置于等待状态,那么该线程在很长时间内就不能再次被调度。实际上,在编写得不好的应用程序中,该线程永远不会再次被赋予CPU时间。TryEnterCriticalSection函数决不允许调用线程进入等待状态。它的返回值能够指明调用线程是否能够获得对资源的访问权。TryEnterCriticalSection发现该资源已经被另一个线程访问,它就返回FALSE。在其他所有情况下,它均返回TRUE。运用这个函数,线程能够迅速查看它是否可以访问某个共享资源,如果不能访问,那么它可以继续执行某些其他操作,而不必进行等待。如果TryEnterCriticalSection函数确实返回了TRUE,那么CRITICAL_SECTION的成员变量已经更新。Windows98没有可以使用的TryEnterCriticalSection函数的实现代码。



 

文章出处:   发表时间:2004-11-20 12:16:55

5条数据记录,分5页显示 上一页 < 1 2 3 4 [5] > 下一页
相关文章  
[源码下载] · comicq源代码
[书籍教程] · VC++ 6.0数据库系统开发实例导航
[书籍教程] · Delphi 7数据库编程学习捷径
[书籍教程] · Delphi百例精解
[书籍教程] · DELPHI综合开发文档

相关评论  
 当前没有评论!
请登陆后再来发表评论!
当前位置:源码资源网首页 > 开发文档首页 > 线程的基础知识
会员升级 | 广告服务 | 网站开发 | 联系我们 | 网站动态 | 客户反馈

CodeRes.com 保留所有权利 2004
本站所有资源仅供学习参考,版权归原作者所有,如侵犯了您的权益请与我们联系