多核编程中的条件同步模式【含代码】

lehedele 2009-02-05 11:20:44
加精
在多线程编程中,当对共享资源进行操作时,需要使用同步(通常是锁或原子操作)来进行保护,以避免数据竞争问题。不幸的是,同步操作的开销非常大,比如对一个整数变量进行加法操作,那么同步操作的开销是加法操作的上百倍以上。

有没有办法可以减少这种同步操作的开销呢?如果能设计出更快的锁或更快的原子操作来,那么这种开销自然就减少了。以目前的技术来看,最快速的原子操作耗时也是普通加法操作的上百倍,所以从这方面着手是非常困难的。

那么能不能从软件算法的角度来减少同步操作的开销呢?答案是当然可以,基本思想是减少使用同步的次数,比如原来要使用同步1000次,现在改为在满足一定条件下才使用同步,只需要10次,那么同步的开销平摊下来就被减少了100倍,效率大大提高了。下面先来看一个共享队列例子。

一个普通的共享队列通常都是使用锁来实现,当然也有用CAS原子操作来实现的,这里只讨论用锁来实现的共享队列。

在有锁保护的共享队列中,在队列的进队和出队操作时,通常都是使用锁来进行保护的,一个典型的使用锁保护的出队操作伪代码如下:

template class <T>

Locked_DeQueue(T &data)

{

Lock();

DeQueue(data); //调用串行的出队操作

Unlock();

}



在使用上面的Locked_DeQueue()函数时,每调用一次,就会发生一次锁操作。事实上,并不是每次都需要加锁操作的,比如队列为空时,这时实际上是不需要进行出队操作的,完全可以采取的一定的方法避免锁操作,但是采用上面的Locked_DeQueue()函数无法避免锁操作,这就需要对上面的函数进行改进。

一种最容易想到的方面就是先判断队列是否为空,如果不为空才使用锁保护进行出队操作。代码如下:

template class <T>

Locked_DeQueue_a(T &data)

{

If ( !IsEmpty() )

{

Lock();

DeQueue(data); //调用串行的出队操作

Unlock();

}

}



上面的Locked_DeQueue_a()函数的一个关键之处是IsEmpty()函数必须不能使用锁操作,否则不仅没有减少同步开销,反而将同步开销增大了近一倍。

如何来使得IsEmtpy()函数不用锁操作呢,以数组实现的环行队列为例,在判断队列是否为空时,其基本方法是判断队首指针是否等于队尾指针。伪代码如下:

INT IsEmpty()

{

Lock()

if ( 队首指针 == 队尾指针 )

{

Unlock();

return 1; //为空

}

else

{

Unlock();

return 0; //非空

}

}



由于队首指针和队尾指针在进队或出队操作时会发生改变,因此在上面的IsEmpty()函数中,需要使用锁保护,那么如何去掉这层锁保护呢?

基本的方法是设一个标志变量EmptyFlag,在进队和出队操作中,当队列为空时,标志变量的值置为1,队列非空时,标志变量的值置为0。这样判断队列是否为空就可以通过EmptyFlag单个变量来进行,而单个变量的读写可以使用原子操作来实现,使得读操作和普通操作一样不存在同步操作。

下面是使用EmptyFlag变量实现的出队操作。

template class <T>

Locked_DeQueue_b(T &data)

{

if ( EmptyFlag )

{

return;

}

Lock();

if ( !EmptyFlag ) //Lock()前, 其他线程可能修改了标志

{

DeQueue(data); //调用串行的出队操作

if ( 队首指针 == 队尾指针 )

{

//出队后,队列变空,使用原子操作将EmptyFlag置为1

AtomicIncrement(&EmptyFlag);

}

}

Unlock();

}



队列的是否为空函数可以使用下面的完全不需要同步的实现。

INT IsEmpty()

{

return EmptyFlag;

}



从Locked_DeQueue_b()函数的实现可以看出,如果队列本来为空的情况下,它只判断一个EmptyFlag就返回了,不会调用锁操作,减少了同步使用的次数,并且在IsEmpty()函数中,根本不需要使用同步,这对于那些需要频繁判断队列是否为空的使用场景,有很好的效果。

比如对于动态任务调度,假设使用普通的有锁的共享队列。当一个线程私有队列为空时,需要去偷取其他线程的共享队列中的任务,如果偷取的队列为空则发生了一次锁操作,此时需要再偷另外一个队列的任务,如果这个队列仍然为空则又要一次锁操作,一次获取任务的操作中将可能出现多次加锁解锁的情况。通过上面讲的条件同步方法就可以在偷取取一个任务时,只要一次锁操作就可以实现。

上面讲的条件同步模式非常适应于具有状态机性质的场合,只有在发生状态切换(例如队列中空或非空的状态的切换)时才使用同步,通过对状态变量(例如EmptyFlag)的操作来替代其他非状态变量(例如队首指针和队尾指针)的操作,减少同步的使用。


本文转自“英特尔软件博客”作者:周伟民
...全文
867 26 打赏 收藏 转发到动态 举报
写回复
用AI写文章
26 条回复
切换为时间正序
请发表友善的回复…
发表回复
kingjo002 2012-10-15
  • 打赏
  • 举报
回复
进程同步?
macklau 2010-04-30
  • 打赏
  • 举报
回复
学习了
txdy1ok 2009-02-10
  • 打赏
  • 举报
回复
ok
schumacher851 2009-02-09
  • 打赏
  • 举报
回复
学习中……
xue_ligang 2009-02-09
  • 打赏
  • 举报
回复
study
m6807 2009-02-08
  • 打赏
  • 举报
回复
学习
yangbo_cuit 2009-02-07
  • 打赏
  • 举报
回复
学习……
saxqqq 2009-02-07
  • 打赏
  • 举报
回复
他是复制别人的 他能懂吗./
elssann 2009-02-07
  • 打赏
  • 举报
回复
有笔误,最后一句应该为“此时B线程还认为队列是非空。 ”
a475546973 2009-02-07
  • 打赏
  • 举报
回复
不晓得,不懂
elssann 2009-02-07
  • 打赏
  • 举报
回复
这个文章和代码都有问题。


======================
======================

下面是使用EmptyFlag变量实现的出队操作。

template class <T>

Locked_DeQueue_b(T &data)

{

if ( EmptyFlag )

{

return;

}

Lock();

if ( !EmptyFlag ) //Lock()前, 其他线程可能修改了标志

{

DeQueue(data); //调用串行的出队操作

if ( 队首指针 == 队尾指针 )

{

//出队后,队列变空,使用原子操作将EmptyFlag置为1

AtomicIncrement(&EmptyFlag);

}

}

Unlock();

}



队列的是否为空函数可以使用下面的完全不需要同步的实现。

INT IsEmpty()

{

return EmptyFlag;

}



==============================分割线================================
OK, 现在有A 线程和B线程,A线程从队列里面取,B线程往队列里面塞.

按照这个文章的说法, 现在假设这样一种情况:
队列里面还剩下两个元素 n1, n2。 A线程把n1从队列里面取出来了,这时候B线程打算把 n3塞进队列,
首先调用IsEmpty(),结果队列不为空,代码类似:

B线程:
if (IsEmpty() == false)
{
putQueue(n3);
}



但是在IsEmpty()返回后,putQueue调用前,A线程又把n2从队列里面取了出来,这时候队列为空,
此时B线程还认为队列是空。

iolya 2009-02-07
  • 打赏
  • 举报
回复
学习
NickCheng 2009-02-06
  • 打赏
  • 举报
回复
牛人辣2!
lizhi1987916 2009-02-06
  • 打赏
  • 举报
回复
没学过这个,不过看下,长点见识
LichKing 2009-02-06
  • 打赏
  • 举报
回复
study
weilong147247943 2009-02-06
  • 打赏
  • 举报
回复
顶··
lizhi1987916 2009-02-06
  • 打赏
  • 举报
回复
不错,顶
tncqsy 2009-02-06
  • 打赏
  • 举报
回复
不错,要是早看见就好了
israelsq 2009-02-06
  • 打赏
  • 举报
回复
4楼写的是什么东东,能解释下么?
way6666 2009-02-06
  • 打赏
  • 举报
回复
学习
加载更多回复(5)

695

社区成员

发帖
与我相关
我的任务
社区描述
提出问题
其他 技术论坛(原bbs)
社区管理员
  • community_281
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧