找回密码
 用户注册

QQ登录

只需一步,快速开始

查看: 4149|回复: 0

同步锁的使用

[复制链接]
发表于 2008-9-21 14:47:11 | 显示全部楼层 |阅读模式
关于锁的使用
在网络开发的过程当中, 相信大家经常要用到多线程.众所周知, 多线程程序需要对共享资源进行同步访问, 需要用到锁.
关于锁的使用要谨慎,稍有不对便会造成饥饿,死锁, 从而导致服务死掉.
下面我就以自己的个人体会介绍一些经验, 希望能起到抛砖引玉的作用
1. 定界锁
  大家都知道, 对于同步资源的访问分为3步: 首先锁定资源, 然后访问资源, 最后释放资源, 这样就可以避免多个线程同时操作同一个资源, 等到访问结束之后, 就释放锁, 以便其他的线程能够访问该资源.事例代码如下:
LOCK lock; // LOCK为自定义的锁,实现了acquire和release接口
RESOURCE resource; // 同步资源
int ThreadFunc(void* p)  //线程函数
{
  lock.acquire();
  //VisitResource, 此处为访问资源的代码
...
...
...
  lock.release();
}
大家可能觉得上面的代码很正常, 没什么好怀疑的, 但事实上问题没这么简单, 因为在访问资源的代码中可能抛出异常, 或者产生错误而直接返回了, 这样锁就没有得到合理的释放, 因而导致了死锁.啊?正常的代码也出现了问题, 该怎么办呢?
这个问题有这样一种解决方案,
class Gurad
{
public:
GuradLOCK& lock)
{
  m_pLock = &lock;
  m_pLock->acquire();  
}
~Gurad()
{
  m_pLock->release();
}
private:
LOCK* m_pLock;
};
这样再使用同步资源的时候就变成下面这样了:
int ThreadFunc(void* p)
{
Guard temp(lock);
//访问同步资源
}
这样在函数的结尾, Guard的析构函数会自动释放锁, 即使在访问同步资源的时候出现了异常, 或者由于中间出现了错误而不经意的返回了, 也会调用Gurad的析够函数,从而保证了资源同步锁的合理释放.
(C++中的异常处理机制是基于堆栈辗转的, 也是一种变相的返回方式, 推荐大家参考下Inside the C++ Object Model).
可能会有朋友说, 如果在这个函数中我只想在某个区间内锁定, 也就是说想自主控制锁的生命期, 用玩了就释放,而不是让锁在函数区间内一直锁定, 那该怎么办呢?
让我们把这个代码再一点点完善:
class Gurad
{
public:
GuradLOCK& lock)
{
  m_pLock = &lock;
  m_bOwner = true;
  m_pLock->acquire();  
}
  
~Gurad()
{
  if(m_bOwner)
   m_pLock->release();
}
void release()
{
  if(m_bOwner)
   m_pLock->release();
}
private:
LOCK* m_pLock;
BOOL  m_bOwner;
};

int ThreadFunc(void* p)
{
Guard temp(lock);
//访问同步资源

temp.release();
//继续进行事务处理
}
这样就可以控制锁的生命周期了.
事情好像已经很好解决了, 再想想, 如果你写的组件要用于单线程呢, 虽然只是在同步与不同步之间有一个很小的差异, 但是细节问题散落在代码各处, 这就需要重新修改代码, 编译, 调试, 很麻烦;我们知道, 单线程不需要同步, 这样就可以把单线程与多线程之间的不同点抽象出来, 用模板技术:

template < typename LOCK>
class Gurad
{
public:
Gurad(LOCK& lock)
{
  m_pLock = lock;
  m_bOwner = true;
  m_pLock->acquire();
}
~Gurad()
{
  if(m_bOwner)
   m_pLock->release();
}
void release()
{
  if(m_bOwner)
   m_pLock->release();
}
private:
LOCK* m_pLock;
BOOL  m_bOwner;
}
其中LOCK是我们自己定义的锁, 当用于多线程时, LOCK要实现对应的锁定和释放锁接口, 如果时单线程呢LOCK对应的acquire和release都是空操作 , 如下:
class NULL_LOCK
{
public:
void acquire()
{
}
void release()
{
}
}
这样在单线程和 多线程之间进行切换的时候, 只需传递给Guard模板不同的锁参数就可以了, 是不是很简单呢?
当然了, 这里涉及到基于策略的组件思想, 建议有兴趣的朋友可以研究下Modern C++ Design
您需要登录后才可以回帖 登录 | 用户注册

本版积分规则

Archiver|手机版|小黑屋|ACE Developer ( 京ICP备06055248号 )

GMT+8, 2024-5-3 21:22 , Processed in 0.016708 second(s), 6 queries , Redis On.

Powered by Discuz! X3.5

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表