`
hududanyzd
  • 浏览: 780371 次
文章分类
社区版块
存档分类
最新评论

谈谈多线程的思维方式

 
阅读更多
前段时间仔细看过些关于多线程方面的资料,项目中用到线程的地方也不少,可是,当看了Jeffrey的一篇关于锁的文章后,发现自己虽然一直都在使用多线程,但是缺少了做多线程编程需要的思维!所以想从Jeffrey的Optex(锁)入手,来谈谈我从其中体会的东西。

在NET中,我们用的最多的锁机制就是lock,用起来很简单,短短几行程序就可以实现,例如:

publicclassTestThreading
{
privateSystem.ObjectlockThis=newSystem.Object();
publicvoidFunction()
{
lock(lockThis)
{
//Accessthread-sensitiveresources.
}
}
}
其实我们也明白,lock并不是锁,而是MS提供的一个简便式的写法,真正实现的是Monitor类中的Enter和Exit方法,既然提到了Monitor类也就说下有个需要注意的地方:
Pulse和PulseAll方法,这两个方法就是把锁状态将要改变的消息通知给等待队列中的线程,不过这时如果等待队列中没有线程,那么该方法就会一直等待下去,直到有等待的线程进入队列,也就是说该方法可能造成类试死锁的情况出现。

上面的lock + 线程(Thread和ThreadPool) = 多线程编程(N%)!?
对于该公式我曾经的N是80,现在是20。其中有很多东西影响我,让我从80->20,下面的Optex就是一个入口点。


publicsealedclassOptex:IDisposable{
privateInt32m_Waiters=0;
privateSemaphorem_WaiterLock=newSemaphore(0,Int32.MaxValue);

publicOptex(){}
publicvoidDispose(){
if(m_WaiterLock!=null)
{
m_WaiterLock.Close();
m_WaiterLock=null;
}
}

publicvoidEnter(){
Thread.BeginCriticalRegion();
//AddourselftothesetofthreadsinterestedintheOptex
if(Interlocked.Increment(refm_Waiters)==1){
//Ifwewerethefirstthreadtoshowinterest,wegotit.
return;
}

//AnotherthreadhastheOptex,weneedtowaitforit
m_WaiterLock.WaitOne();
//WhenWaitOnereturns,thisthreadnowhastheOptex
}

publicvoidExit(){
//SubtractourselffromthesetofthreadsinterestedintheOptex
if(Interlocked.Decrement(refm_Waiters)>0){
//Otherthreadsarewaiting,wake1ofthem
m_WaiterLock.Release(1);
}
Thread.EndCriticalRegion();
}
}
看完上面的代码,让我增加了两点认识:
1、Thread.BeginCriticalRegion()和Thread.EndCriticalRegion();
因为这段时间正好看了一本多线程编程的书,既然将上面方法认为是进入临界区和退出临界区,对于临界区而言,进入该区的数据,在没有退出之前,如果临界区外的程序需要使用它,那么就必须出于等待。所以觉得已经使用临界区,为什么还要使用Semaphore?!
可是,MS只是取了个相同的名字,做的事情完全不同,上面两个方法完全没有临界区的概念,它只是设置一个区域(Begin到End之间),表示该区域内发生线程中断或未处理的异常会影响整个应用程序域。

2、m_Waiters的作用

一开始以为在Enter的时候,直接写上:
m_WaiterLock.WaitOne();

Exit的时候,写上:
m_WaiterLock.Release(1);
这样就可以了。m_Waiters有什么意义?!
优化性能,Semaphore是内核对象,我们都知道,要尽量少的进入内核模式,因为这是很消耗性能,所以尽量少的使用内核对象。m_Waiters的意义就在这里,如果只有一个线程使用该锁对象的时候,是不需要去获取和释放的。 OK,上述的东西都是铺垫,铺完了也就进入主题了!

多线程的思维

namespaceThreadConcurrent.Lock
{
publicsealedclassOptex:IDisposable
{
///<summary>
///琐的状态
///</summary>
privateInt32m_LockState=c_lsFree;

///<summary>
///自由状态
///</summary>
privateconstInt32c_lsFree=0x00000000;

///<summary>
///被拥有状态
///</summary>
privateconstInt32c_lsOwned=0x00000001;

///<summary>
///等待的线程数
///</summary>
privateconstInt32c_1Waiter=0x00000002;

privateSemaphorem_WaiterLock=newSemaphore(0,Int32.MaxValue);

#region构造函数

///<summary>
///
///</summary>
publicOptex(){}

#endregion

///<summary>
///请求锁
///</summary>
publicvoidEnter()
{
Thread.BeginCriticalRegion();
while(true)
{
Int32ls=InterlockedOr(refm_LockState,c_lsOwned);

//自由状态
if((ls&c_lsOwned)==c_lsFree)return;

//增加等待的线程数
if(IfThen(refm_LockState,ls,ls+c_1Waiter))
{
m_WaiterLock.WaitOne();
}
}
}

publicvoidExit()
{
//释放琐
Int32ls=InterlockedAnd(refm_LockState,~c_lsOwned);

//无等待的线程
if(ls==c_lsOwned)
{
}
else
{
ls&=~c_lsOwned;
if(IfThen(refm_LockState,ls&~c_lsOwned,ls-c_1Waiter))
{
m_WaiterLock.Release(1);
}
else
{
}
}
Thread.EndCriticalRegion();
}

#region原子化操作

///<summary>
///与操作
///</summary>
///<paramname="target"></param>
///<paramname="with"></param>
///<returns></returns>
privatestaticInt32InterlockedAnd(refInt32target,Int32with)
{
Int32i,j=target;
do
{
i=j;
j=Interlocked.CompareExchange(reftarget,i&with,i);
}while(i!=j);
returnj;
}

///<summary>
///或操作
///</summary>
///<paramname="target"></param>
///<paramname="with"></param>
///<returns></returns>
privatestaticInt32InterlockedOr(refInt32target,Int32with)
{
Int32i,j=target;
do
{
i=j;
j=Interlocked.CompareExchange(reftarget,i|with,i);
}while(i!=j);
returnj;
}

#endregion

privatestaticBooleanIfThen(refInt32val,Int32@if,Int32then)
{
return(Interlocked.CompareExchange(refval,@then,@if)==@if);
}

privatestaticBooleanIfThen(refInt32val,Int32@if,Int32then,outInt32prevVal)
{
prevVal=Interlocked.CompareExchange(refval,@then,@if);
return(prevVal==@if);
}

///<summary>
///释放资源
///</summary>
publicvoidDispose()
{
if(m_WaiterLock!=null)
{
m_WaiterLock.Close();
m_WaiterLock=null;
}
}
}
}
对于上面的这个代码,我晕眩了好一段时间,不过当我真正理解的时候,从晕眩中学到了做多线程编程应该具备的思维方式。
首先从简单的理解开始谈,
1、原子化操作
对于InterLocked类,曾经也知道,但是却用的很少,不过从该代码中知道,在多线程的编程中对共享数据的写入操作,一定要达到原子性。至于如何做到这点,InterlockedAnd和InterlockedOr做了很好的诠释:
While循环的目的就是保证target值以最新的值做与操作,如果传入的值在执行的过程被其他线程改变的话,那么是不会退出该循环的,并会利用改变后的值重新做次与操作。

2、理解Enter和Exit
这两个方法很难写出来解释,用图是最清晰的。


曾经的晕眩:
1、Enter方法中为什么存在循环,为什么不是执行完waitone就结束,必须m_lockState等于c_IsFree的时候才结束?
线程的执行并不完全按照先前排好的顺序去执行,有时会发生一些特殊的情况来使改变线程的调度顺序,所以就可能会出现上图灰色部分的情况,则为了解决该可能发生的问题(概率很小)循环机制就出现了。
2、为什么在WaitOne和Release之前,除了增加和减少等待者外,还需要判断m_lockstate是否改变(进入Enter到执行Waitone前的这段时间)?
一般性的思维:


该程序的思维:

这样做的好处就是尽量少的操作内核对象,提高性能!

多线程编程虽然复杂,但是我觉得很有意思和挑战性,而且随着硬件的发展,多线程编程会更加重要,既然已经上路就让我们走到尽头!
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics