两种自旋锁的设置
- 操作系统级别自旋锁的设置, 例如在C++11及以后的版本中, 自带线程管理库, 可以定义为:
define CV_YIELD() std::this_thread::yield()
, 此时进入CV_YIELD()
, 线程释放CPU, 线程被阻塞, 等待被唤醒. - CPU级别的自旋锁的设置, 与使用的CPU架构有关, 以X86为例, X86对应的汇编代码如下:
static inline void cv_non_sse_mm_pause() { __asm__ __volatile__ ("rep; nop"); }
, 使用一个内联函数cv_non_sse_mm_pause
, 然后重复不断的执行nop
, 即进行CPU占用的忙等待.
线程体的两种等待形式
主动等待
主动等待的目的
当该线程被创建, 线程获取CPU之后, 还未收到has_wake_signal
, (未收到可以开始执行的信号), 此时, 线程并不会直接释放CPU, 而是会忙等一定的时间, 这个时间与参数OPENCV_THREAD_POOL_ACTIVE_WAIT_WORKER
成正比. 这样做, 用户可以选择配置忙等的时间, 可以更加灵活的使用CPU.
主动等待的过程
主动等待循环的次数 CV_WORKER_ACTIVE_WAIT
就是环境变量OPENCV_THREAD_POOL_ACTIVE_WAIT_WORKER
, 在未达到循环次数时, 调用 CPU 的自旋锁, 使线程陷入忙等, 在达到循环次数之后, 调用操作系统的自旋锁, 线程被阻塞.
// 主动等待
CV_LOG_VERBOSE(NULL, 5, "Thread: ... loop iteration: allow_active_wait=" << allow_active_wait << " has_wake_signal=" << has_wake_signal);
// 如果允许忙等, 并且设置的环境变量 OPENCV_THREAD_POOL_ACTIVE_WAIT_WORKER > 0
if (allow_active_wait && CV_WORKER_ACTIVE_WAIT > 0)
{
allow_active_wait = false;
// 在 OPENCV_THREAD_POOL_ACTIVE_WAIT_WORKER 时间内忙等资源, 实际上是等待收到信号 has_wake_signal
for (int i = 0; i < CV_WORKER_ACTIVE_WAIT; i++)
{
// 如果已经收到唤醒信号, 表示可以开始执行任务, 退出循环
if (has_wake_signal)
break;
if (CV_ACTIVE_WAIT_PAUSE_LIMIT > 0 && (i < CV_ACTIVE_WAIT_PAUSE_LIMIT || (i & 1)))
CV_PAUSE(16);
else
CV_YIELD();
}
}
被动等待
被动等待的实现方式是使用互斥锁, 配合C++多线程中的 pthread_cond_wait
方法. 使用条件变量与互斥锁, 这种方式的好处是:
- 避免互斥锁的忙等待(互斥锁只能忙等)
- 简化同步逻辑, 以及高效的线程唤醒.
多线程条件变量等待pthread_cond_wait
与互斥锁的配合使用
条件变量是利用线程间共享的全局变量进行同步的一种机制, 主要包括两个动作:
- 一个线程等待"条件变量的条件成立"而挂起;
- 另一个线程使"条件成立"(给出条件成立信号).
为了防止竞争, 条件变量的使用总是和一个互斥锁结合在一起, 这样既可以避免了直接使用互斥锁导致的忙等, 也可以实现资源的同步, 防止资源使用冲突.
使用模板
互斥锁与条件变量的声明
// 声明全局变量
// 线程互斥锁
pthread_mutex_t mutex;
// 线程资源等待条件变量
pthread_cond_t q_ready;
// 初始化
pthread_mutex_init(&mutex, NULL);
pthread_cond_init(&q_ready, NULL);
等待资源的线程
pthread_mutex_t qlock;
pthread_cond_t qready;
/************pthread_cond_wait()的使用方法**********/
pthread_mutex_lock(&mutex); /*lock*/
// 等待某资源,并以q_ready作为条件通知我们
pthread_cond_wait(&q_ready, &mutex); /*block-->unlock-->wait() return-->lock*/
// 修改互斥锁 mutex 保护的资源
pthread_mutex_unlock(&qlock); /*unlock*/
释放资源的线程
// 被动等待开始, 获取互斥锁
pthread_mutex_lock(&mutex);
#ifdef CV_PROFILE_THREADS
stat.threadWait = getTickCount();
#endif
// 进入循环等待, 等待收到唤醒信号 has_wake_signal
while (!has_wake_signal) // to handle spurious wakeups
{
//CV_LOG_VERBOSE(NULL, 5, "Thread: wait (sleep) ...");
// 如果使用全局的唤醒信号
#if defined(CV_USE_GLOBAL_WORKERS_COND_VAR)
pthread_cond_wait(&thread_pool.cond_thread_wake, &mutex);
#else
isActive = false;
// 这里会释放CPU资源, 然后等待环境变量, 同时释放互斥锁,
pthread_cond_wait(&cond_thread_wake, &mutex);
isActive = true;
#endif
CV_LOG_VERBOSE(NULL, 5, "Thread: wake ... (has_wake_signal=" << has_wake_signal << " stop_thread=" << stop_thread << ")")
}
#ifdef CV_PROFILE_THREADS
stat.threadWake = getTickCount();
#endif
CV_LOG_VERBOSE(NULL, 5, "Thread: checking for new job");
if (CV_WORKER_ACTIVE_WAIT_THREADS_LIMIT == 0)
allow_active_wait = true;
Ptr<ParallelJob> j_ptr; swap(j_ptr, job);
has_wake_signal = false; // 资源已经获取完成, 可以开始执行
pthread_mutex_unlock(&mutex);
标签:cond,互斥,线程,pthread,mutex,多线程,OPEN,CV
From: https://www.cnblogs.com/wevolf/p/18229545