循环原子锁

原文:http://www.cppblog.com/woaidongmao/archive/2008/04/16/47229.html

InterlockedExchange和InterlockedExchangePointer能够以原子操作方式用第二个参数中传递的值来取代第一个参数中传递的当前值。如果是32位应用程序,两个函数都能用另一个32位值取代一个32位值。但是,如果是个64位应用程序,那么InterlockedExchange能够取代一个32位值,而InterlockedExchangePointer则取代64位值。两个函数都返回原始值。当实现一个循环锁时,InterlockedExchange是非常有用的:

  1. // Global variable indicating whether a shared resource is in use or not
  2. BOOL g_fResourceInUse = FALSE;
  3. ...

  4. void Func1()
  5. {
  6. //Wait to access the resource.
  7. while(InterlockedExchange(&g_fResourceInUse, TRUE) == TRUE)
  8. Sleep(0);

  9. //Access the resource.

  10. ...

  11. //We no longer need to access the resource.
  12. InterlockedExchange(&g_fResourceInUse, FALSE);
  13. }



while循环是循环运行的,它将g_fResourceInUse中的值改为TRUE,并查看它的前一个值,以了解它是否是TRUE。如果这个值原先是FALSE,那么该资源并没有在使用,而是调用线程将它设置为在用状态并退出该循环。如果前一个值是TRUE,那么资源正在被另一个线程使用,while循环将继续循环运行。

如果另一个线程要执行类似的代码,它将在while循环中运行,直到g_fResourceInUse重新改为FALSE。调用函数结尾处的InterlockedExchange,可显示应该如何将g_fResourceInUse重新设置为FALSE。

当使用这个方法时必须格外小心,因为循环锁会浪费CPU时间。CPU必须不断地比较两个值,直到一个值由于另一个线程而“奇妙地”改变为止。另外,该代码假定使用循环锁的所有线程都以相同的优先级等级运行。也可以把执行循环锁的线程的优先级提高功能禁用(通过调用SetProcessPriorityBoost或setThreadPriorityBoost函数来实现之)

此外,应该保证将循环锁变量和循环锁保护的数据维护在不同的高速缓存行中(本章后面部分介绍)。如果循环锁变量与数据共享相同的高速缓存行,那么使用该资源的CPU将与试图访问该资源的任何CPU争用高速缓存行。

应该避免在单个CPU计算机上使用循环锁。如果一个线程正在循环运行,它就会浪费前一个CPU时间,这将防止另一个线程修改该值。我在上面的while循环中使用了Sleep,从而在某种程度上解决了浪费CPU时间的问题。如果使用Sleep,你可能想睡眠一个随机时间量;每次请求访问该资源均被拒绝时,你可能想进一步延长睡眠时间。这可以防止线程浪费CPU时间。根据情况,最好是全部删除对Sleep的调用。或者使用对SwitchToThread(Windows98中没有这个函数)的调用来取代它。勇于试验和不断纠正错误,是学习的最好方法。

循环锁假定,受保护的资源总是被访问较短的时间。这使它能够更加有效地循环运行,然后转为内核方式并进入等待状态。许多编程人员循环运行一定的次数(比如400次),如果对资源的访问仍然被拒绝,那么该线程就转为内核方式,在这种方式下,它要等待(不消耗CPU时间),直到该资源变为可供使用为止。这就是关键部分实现的方法。

循环锁在多处理器计算机上非常有用,因为当一个线程循环运行的时候,另一个线程可以在另一个CPU上运行。但是,即使在这种情况下,也必须小心。不应该让线程循环运行太长的时间,也不能浪费更多的CPU时间。本章后面将进一步介绍循环锁。

本文链接:https://my.lmcjl.com/post/10474.html

展开阅读全文

4 评论

留下您的评论.