关闭

C++11中的mutex, lock, condition variable实现分析

发表于:2016-9-22 09:48

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:hengyunabc    来源:51Testing软件测试网采编

  可以看到多参数的std::lock的实现是:
  先获取一个锁,然后再调用std::try_lock去获取剩下的锁,如果失败了,则下次先获取上次失败的锁。
  重复上面的过程,直到成功获取到所有的锁。
  上面的算法用比较巧妙的方式实现了参数的轮转。
  std::timed_mutex
  std::timed_mutex   是里面封装了mutex和condition,这样就两个函数可以用:
  try_lock_for
  try_lock_until
  实际上是posix的mutex和condition的包装。
class timed_mutex
{
mutex              __m_;
condition_variable __cv_;
bool               __locked_;
public:
timed_mutex();
~timed_mutex();
private:
timed_mutex(const timed_mutex&); // = delete;
timed_mutex& operator=(const timed_mutex&); // = delete;
public:
void lock();
bool try_lock() _NOEXCEPT;
template
_LIBCPP_INLINE_VISIBILITY
bool try_lock_for(const chrono::duration& __d)
{return try_lock_until(chrono::steady_clock::now() + __d);}
template
bool try_lock_until(const chrono::time_point& __t);
void unlock() _NOEXCEPT;
};
template
bool
timed_mutex::try_lock_until(const chrono::time_point& __t)
{
using namespace chrono;
unique_lock __lk(__m_);
bool no_timeout = _Clock::now()
std::recursive_mutex和std::recursive_timed_mutex
  这两个实际上是std::mutex和std::timed_mutex 的recursive模式的实现,即锁得获得者可以重复多次调用lock()函数。
  和posix mutex里的recursive mutex是一样的。
  看下std::recursive_mutex的构造函数就知道了。
recursive_mutex::recursive_mutex()
{
pthread_mutexattr_t attr;
int ec = pthread_mutexattr_init(&attr);
if (ec)
goto fail;
ec = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
if (ec)
{
pthread_mutexattr_destroy(&attr);
goto fail;
}
ec = pthread_mutex_init(&__m_, &attr);
if (ec)
{
pthread_mutexattr_destroy(&attr);
goto fail;
}
ec = pthread_mutexattr_destroy(&attr);
if (ec)
{
pthread_mutex_destroy(&__m_);
goto fail;
}
return;
fail:
__throw_system_error(ec, "recursive_mutex constructor failed");
}
std::cv_status
  这个用来表示condition等待返回的状态的,和上面的三个表示lock的状态的用途差不多。
  enum cv_status
  {
  no_timeout,
  timeout
  };
  std::condition_variable
  包装了posix condition variable。
class condition_variable
{
pthread_cond_t __cv_;
public:
condition_variable() {__cv_ = (pthread_cond_t)PTHREAD_COND_INITIALIZER;}
~condition_variable();
private:
condition_variable(const condition_variable&); // = delete;
condition_variable& operator=(const condition_variable&); // = delete;
public:
void notify_one() _NOEXCEPT;
void notify_all() _NOEXCEPT;
void wait(unique_lock& __lk) _NOEXCEPT;
template
void wait(unique_lock& __lk, _Predicate __pred);
template
cv_status
wait_until(unique_lock& __lk,
const chrono::time_point& __t);
template
bool
wait_until(unique_lock& __lk,
const chrono::time_point& __t,
_Predicate __pred);
template
cv_status
wait_for(unique_lock& __lk,
const chrono::duration& __d);
template
bool
wait_for(unique_lock& __lk,
const chrono::duration& __d,
_Predicate __pred);
typedef pthread_cond_t* native_handle_type;
_LIBCPP_INLINE_VISIBILITY native_handle_type native_handle() {return &__cv_;}
private:
void __do_timed_wait(unique_lock& __lk,
chrono::time_point) _NOEXCEPT;
};
  里面的函数都是符合直觉的实现,值得注意的是:
  cv_status是通过判断时间而确定的,如果超时的则返回cv_status::timeout,如果没有超时,则返回cv_status::no_timeout。
  condition_variable::wait_until函数可以传入一个predicate,即一个用户自定义的判断是否符合条件的函数。这个也是很常见的模板编程的方法了。
  template
  cv_status
  condition_variable::wait_until(unique_lock& __lk,
  const chrono::time_point& __t)
  {
  using namespace chrono;
  wait_for(__lk, __t - _Clock::now());
  return _Clock::now()
  bool
  condition_variable::wait_until(unique_lock& __lk,
  const chrono::time_point& __t,
  _Predicate __pred)
  {
  while (!__pred())
  {
  if (wait_until(__lk, __t) == cv_status::timeout)
  return __pred();
  }
  return true;
  }
  std::condition_variable_any
  std::condition_variable_any的接口和std::condition_variable一样,不同的是std::condition_variable只能使用std::unique_lock,而std::condition_variable_any可以使用任何的锁对象。
  下面来看下为什么std::condition_variable_any可以使用任意的锁对象。
class _LIBCPP_TYPE_VIS condition_variable_any
{
condition_variable __cv_;
shared_ptr  __mut_;
public:
condition_variable_any();
void notify_one() _NOEXCEPT;
void notify_all() _NOEXCEPT;
template
void wait(_Lock& __lock);
template
void wait(_Lock& __lock, _Predicate __pred);
template
cv_status
wait_until(_Lock& __lock,
const chrono::time_point& __t);
template
bool
wait_until(_Lock& __lock,
const chrono::time_point& __t,
_Predicate __pred);
template
cv_status
wait_for(_Lock& __lock,
const chrono::duration& __d);
template
bool
wait_for(_Lock& __lock,
const chrono::duration& __d,
_Predicate __pred);
};
  可以看到,在std::condition_variable_any里,用shared_ptr  __mut_来包装了mutex。所以一切都明白了,回顾std::unique_lock,它包装了mutex,当析构时自动释放mutex。在std::condition_variable_any里,这份工作让shared_ptr来做了。
  因此,也可以很轻松得出std::condition_variable_any会比std::condition_variable稍慢的结论了。
  其它的东东:
  sched_yield()函数的man手册:
  sched_yield() causes the calling thread to relinquish the CPU.  The thread is moved to the end of the queue for its
  static priority and a new thread gets to run.
  在C++14里还有std::shared_lock和std::shared_timed_mutex,但是libc++里还没有对应的实现,因此不做分析。
  总结
  llvm libc++中的各种mutex, lock, condition variable实际上是封闭了posix里的对应实现。封装的技巧和一些细节值得细细推敲学习。
  看完了实现源码之后,对于如何使用就更加清晰了。
22/2<12
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号