fangyukuan

永无止境的追求...追求卓越!!!

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

线程需要在下面两种情况下互相进行通信:

•当有多个线程访问共享资源而不使资源被破坏时。

•当一个线程需要将某个任务已经完成的情况通知另外一个或多个线程时。

 

原子访问

所谓原子访问,是指线程在访问资源时能够确保所有其他线程都不在同一时间内访问相同的资源。


背景例子:

 

//Definea global variable.
longg_x = 0;

DWORDWINAPI ThreadFunc1(PVOID pvParam)
{
g_x
++;
return(0);
}

DWORDWINAPI ThreadFunc2(PVOID pvParam)
{
g_x
++;
return(0);
}

 

 

在这个代码中,声明了一个全局变量g _ x,并将它初始化为0。现在,假设创建两个线程,一个线程执行ThreadFunc1,另一个线程执行ThreadFunc2。这两个函数中的代码是相同的,它们都将1添加给全局变量g_x。因此,当两个线程都停止运行时,你可能希望在g _ x中看到2这个值。但是你真的看到了吗?回答是,也许看到了。根据代码的编写方法,你无法说明g _ x中最终包含了什么东西。下面我们来说明为什么会出现这种情况。假设编译器生成了下面这行代码,以便将g _ x递增1:

 

MOV EAX, [g_x]       ;Move the value in g_x into a register.

INC EAX              ;Increment the value in theregister.

MOV [g_x], EAX       ;Store the new value back in g_x.

 

两个线程不可能在完全相同的时间内执行这个代码。因此,如果一个线程在另一个线程的后面执行这个代码,那么下面就是实际的执行情况:

 

MOV EAX, [g_x]      ;Thread 1: Move 0 into a register.

INC EAX             ;Thread 1: Increment the registerto 1.

MOV [g_x], EAX      ;Thread 1: Store 1 back in g_x.

 

MOV EAX, [g_x]      ;Thread 2: Move 1 into a register.

INC EAX             ;Thread 2: Increment the registerto 2.

MOV [g_x], EAX      ;Thread 2: Store 2 back in g_x.

 

当两个线程都将g_x的值递增之后, g_x中的值就变成了2。这很好,并且正是我们希望的:即取出零(0),两次将它递增1,得出的值为2。太好了。不过不要急,Windows是个抢占式多线程环境。一个线程可以随时中断运行,而另一个线程则可以随时继续执行。这样,上面的代码就无法完全按编写的那样来运行。它可能按下面的形式运行:

 

MOV EAX, [g_x]      ;Thread 1: Move 0 into a register.

INC EAX             ;Thread 1: Increment the registerto 1.

 

MOV EAX, [g_x]      ;Thread 2: Move 0 into a register.

INC EAX             ;Thread 2: Increment the registerto 1.

MOV [g_x], EAX      ;Thread 2: Store 1 back in g_x.

 

MOV [g_x], EAX      ;Thread 1: Store 1 back in g_x.

 

互锁函数

如果代码按这种形式来运行, g x中的最后值就不是2,而是你预期的1。

互锁的函数家族提供了我们需要的解决方案。

 

LONG InterlockedExchangeAdd(
LPLONG Addend,
LONG Increment
);

 

 

上面的代码重新编写为下面的形式:

 

//Define a global variable.
longg_x = 0;


DWORDWINAPI ThreadFunc1(PVOID pvParam)
{
InterlockedExchangeAdd(
&g_x, 1);
return(0);
}

DWORDWINAPI ThreadFunc2(PVOID pvParam)
{
InterlockedExchangeAdd(
&g_x, 1);
return(0);
}

 

对于互锁函数,它们运行的速度极快。调用一个互锁函数通常会导致执行几个CPU周期(通常小于50),并且不会从用户方式转换为内核方式(通常这需要执行1000个CPU周期)。

当然,可以使用InterlockedExchangeAdd减去一个值—只要为第二个参数传递一个负值。

 

下面是另外两个互锁函数:

 

LONG InterlockedExchange(
LPLONG Target,
LONG Value
);

PVOID InterlockedExchangePointer(
PVOID
*ppvTarget,
PVOIDpvValue
);

 

本文地址:http://www.cnblogs.com/fangyukuan/archive/2010/09/02/1816267.html

 

 

 

posted on 2010-09-02 17:43  fangyukuan  阅读(1423)  评论(0编辑  收藏  举报