C++11条件变量condition_variable介绍

1. 使用场景

在C++11中,我们可以使用条件变量(condition_variable)实现多个线程间的同步操作;当条件不满足时,相关线程被一直阻塞,直到某种条件出现,这些线程才会被唤醒。

主要成员函数如下:

 

函数的使用介绍:

 1. std::condition_variable::wait()

(1)wait(unique_lock <mutex>&lck)

当前线程的执行会被阻塞,直到收到 notify 为止。

(2)wait(unique_lock <mutex>&lck,Predicate pred)

当前线程仅在pred=false时阻塞;如果pred=true时,不阻塞。

wait()可依次拆分为三个操作:释放互斥锁、等待在条件变量上、再次获取互斥锁

在线程被阻塞时,该函数会自动调用lck.unlock()释放锁,使得其他被阻塞在锁竞争上的线程得以继续执行。另外,一旦当前线程获得通知(notified,通常是另外某个线程调用 notify_* 唤醒了当前线程),wait() 函数也是自动调用 lck.lock(),使得 lck 的状态和 wait 函数被调用时相同。

2. std::condition_variable::wait_for()

与 std::condition_variable::wait() 类似,不过 wait_for 可以指定一个时间段,在当前线程收到通知或者指定的时间 rel_time 超时之前,该线程都会处于阻塞状态。而一旦超时或者收到了其他线程的通知,wait_for 返回,剩下的处理步骤和 wait() 类似。

 

if (m_db_cur_conn_cnt >= m_db_max_conn_cnt)
        {
            // 如果已经到达了,看看是否需要超时等待
            if(timeout_ms <= 0)        // 死等,直到有连接可以用 或者 连接池要退出
            {
                log_info("wait ms:%d\n", timeout_ms);
                m_cond_var.wait(lock, [this] 
                {
                    // log_info("wait:%d, size:%d\n", wait_cout++, m_free_list.size());
                    // 当前连接数量小于最大连接数量 或者请求释放连接池时退出
                    return (!m_free_list.empty()) | m_abort_request;
                });
            } else {
                // return如果返回 false,继续wait(或者超时),  如果返回true退出wait
                // 1.m_free_list不为空
                // 2.超时退出
                // 3. m_abort_request被置为true,要释放整个连接池
                m_cond_var.wait_for(lock, std::chrono::milliseconds(timeout_ms), [this] {
                    // log_info("wait_for:%d, size:%d\n", wait_cout++, m_free_list.size());
                    return (!m_free_list.empty()) | m_abort_request;
                });
                // 带超时功能时还要判断是否为空
                if(m_free_list.empty())     // 如果连接池还是没有空闲则退出
                {
                    return NULL;
                }
            }

            if(m_abort_request) 
            {
                log_warn("have aboort\n");
                return NULL;
            }
        }
        else // 还没有到最大连接则创建连接
        {
            CDBConn *pDBConn = new CDBConn(this);    //新建连接
            int ret = pDBConn->Init();
            if (ret)
            {
                log_error("Init DBConnecton failed\n\n");
                delete pDBConn;
                return NULL;
            }
            else
            {
                m_free_list.push_back(pDBConn);
                m_db_cur_conn_cnt++;
                // log_info("new db connection: %s, conn_cnt: %d\n", m_pool_name.c_str(), m_db_cur_conn_cnt);
            }
        }

 

3. std::condition_variable::notify_one()

notify_one():没有参数、没有返回值。

解除阻塞当前正在等待此条件的线程之一。如果没有线程在等待,则该函数不执行任何操作。如果超过一个,不会指定具体哪一线程。

std::deque<int> q;
std::mutex mu;
std::condition_variable cond;

void function_1() //生产者
{
    int count = 10;
    while (count > 0) 
    {
        std::unique_lock<std::mutex> locker(mu);
        q.push_front(count);
        locker.unlock();
        cond.notify_one();  // Notify one waiting thread, if there is one.
        std::this_thread::sleep_for(std::chrono::seconds(1));
        count--;
    }
}

void function_2() //消费者
{
    int data = 0;
    while (data != 1) 
    {
        std::unique_lock<std::mutex> locker(mu);
        while (q.empty())
            cond.wait(locker); // Unlock mu and wait to be notified
        data = q.back();
        q.pop_back();
        locker.unlock();
        std::cout << "t2 got a value from t1: " << data << std::endl;
    }
}
int main() 
{
    std::thread t1(function_1);
    std::thread t2(function_2);
    t1.join();
    t2.join();
    return 0;
}

4.std::condition_variable::notify_all()

唤醒所有的等待(wait)线程。如果当前没有等待线程,则该函数什么也不做。

核心:

①、在消费者里判断队列是否为空后,如果为空则wait,等待生产者发送notify信号。  // 这里可能会出现虚假唤醒的情况,解决办法使将判断队列是否为空改为while循环,当wait被唤醒后,再检测一次。

②、在生产者那里,如果生产了任务,则发送notify信号,告诉消费者可以试图退出wait,判断队列是否为空,如果有任务则调度处理任务,如果还是空则说明此次notify是错误的,可能是其他地方发出来干扰的,生产者继续wait。

③、流程:

软件开启,生成消费者线程消费队列,应该是一个while循环,在循环里获取锁,再来一个while循环判断条件,如果条件成立则wait,wait会自动释放锁;

此时消费者已经没有锁了,在生产者线程里,获取锁,然后往里面加任务,退出作用域释放锁,然后notify告知消费者退出wait,消费者重新获取锁,然后从队列里取任务;

整个过程,生产者加任务时生产者持有锁,消费者取任务时消费者持有锁。

核心:

①、在消费者里判断队列是否为空后,如果为空则wait,等待生产者发送notify信号

②、在生产者那里,如果生产了任务,则发送notify信号,告诉消费者可以试图退出wait,判断队列是否为空,如果有任务则调度处理任务,如果还是空则说明此次notify是错误的,可能是其他地方发出来干扰的,生产者继续wait。

③、流程:

软件开启,生成消费者线程消费队列,应该是一个while循环,在循环里获取锁,再来一个while循环判断条件,如果条件成立则wait,wait会自动释放锁;

此时消费者已经没有锁了,在生产者线程里,获取锁,然后往里面加任务,退出作用域释放锁,然后notify告知消费者退出wait,消费者重新获取锁,然后从队列里取任务;

整个过程,生产者加任务时生产者持有锁,消费者取任务时消费者持有锁。

posted @ 2022-05-10 21:27  放弃吧  阅读(764)  评论(0)    收藏  举报