6_7_单例_call_once.cpp
#include "hjcommon.hpp"
#include <mutex>
using namespace std;
HJ_NS_USING
class Single // 单例类
{
private:
Single() {}
class GC // 内部嵌套类,析沟中释放单例对象
{
public:
~GC()
{
cout << "delete s_instance." << endl;
delete s_instance;
s_instance = nullptr;
}
};
static void create()
{
cout << "create." << endl;
s_instance = new Single();
static GC gc; // 析沟中释放单例对象
}
public:
static Single *s_instance;
// 单例,方式一(普通方式),建议
// static Single *getInstance()
// {
// if (s_instance==nullptr) // 双重锁定,让已经初始化后的s_instance不要再加锁了,提高效率
// {
// static std::mutex mut;
// unique_lock<mutex> uniqueLock(mut);
// if (s_instance==nullptr)
// {
// s_instance = new Single();
// static GC gc; // 析沟中释放单例对象
// }
// }
// return s_instance;
// }
// 单例,方式二(std::call_once(std::once_flag, func)): call_once也会互斥,比手动写mutex效率感觉要低一些?
static Single *getInstance()
{
if (s_instance==nullptr)
{
static std::once_flag flag; // 标量,实际是一个结构体,表示只调用一次
std::call_once(flag, create);
}
return s_instance;
}
};
Single *Single::s_instance = nullptr;
int main_2_6_7(int argc, char *argv[])
{
Single *single = Single::getInstance();
Single *s2 = Single::getInstance();
// std::call_once(std::once_flag, func) 函数模板,c++11引入,作用:多次执行此代码的情况下,只会调用一次func函数(当第一次执行func后,once_flag会被改为已调用状态)
return 0;
}
6_8_condition_wait_notify.cpp
#include "hjcommon.hpp"
#include <thread>
#include <mutex>
#include <list>
#include <condition_variable>
using namespace std;
HJ_NS_USING
// notify_one() 演示
class C
{
public:
void write()
{
for(int i=0; i<100000; ++i)
{
std::unique_lock<mutex> uniqueLock(m_mutex);
cout << "notify_one write : " << i << endl;
m_list.push_back(i);
m_cond.notify_one(); // 尝试唤醒一个wait(),此时若无wait(),则函数直接返回,不会进行其他任何处理
}
std::unique_lock<mutex> uniqueLock(m_mutex);
isWriteFinish = true;
m_cond.notify_all();
}
void read()
{
while(true)
{
std::unique_lock<mutex> uniqueLock(m_mutex);
// 等待,如果lambda返回false,wait()将解锁互斥,并在此行代码堵塞,堵塞到其他线程调用m_cond.notify_one()函数唤醒为止,
// 此时wait()会不断尝试去锁住互斥,若无法锁住那么会继续阻塞等待下次唤醒,
// 若成功锁住互斥,那么wait()会再次执行lambda,若此时还是返回false,则重复上述步骤,
// 若此时返回true,那么接着执行下行代码
// 如果lambda返回true,那么wait()直接返回,接着执行下行代码
m_cond.wait(uniqueLock, [this] { // 第二个参数只要是可调用对象即可
return !m_list.empty() || isWriteFinish;
});
// m_cond.wait(uniqueLock); // 与m_cond.wait(uniqueLock, lambda)中lambda返回false效果一样,区别是只要被唤醒,就会再次尝试锁住互斥,锁住后接着执行下行代码
if (!m_list.empty())
{
int var = m_list.front();
m_list.pop_front();
cout << "notify_one read : " << var << endl;
}
else
if (isWriteFinish) break;
uniqueLock.unlock(); // 执行无关代码时可以提前解锁,让其他线程继续执行
// ... 无关代码
}
}
private:
std::list<int> m_list;
std::mutex m_mutex;
bool isWriteFinish = false;
std::condition_variable m_cond;
};
int main_2_6_8_1(int argc, char *atgv[])
{
// notify_one() 演示
C a;
thread writeThread(&C::write, &a);
thread readThread(&C::read, &a);
writeThread.join();
readThread.join();
// 条件变量:std::condition_variable、wait()、notify_one()
// std::condition_variable 实际上是一个类,需要包含 #include <condition_variable>
// void condition_variable.wait(...)
// bool/cv_status condition_variable.wait_for(...) // cv_status 枚举值,超时或未超时
// bool/cv_status condition_variable.wait_until(...)
// notify_one() 一次唤醒一个condition_variable.wait()
// notify_all() 唤醒所有condition_variable.wait()
condition_variable cond;
mutex mu;
unique_lock<mutex> ul(mu);
bool is = cond.wait_for(ul, std::chrono::seconds(1), []{
cout << "wait_for." << endl;
return false;
}); // 与wait()类似,区别是如果在阻塞duration时长后,若没有被notify,那么此时会锁住互斥,接着执行lambda,若lambda返回false,不会继续阻塞,而是继续执行后续代码
// condition_variable.wait_until(...) 与wait_for()类似,区别是wait_for是等待一定时长,wait_until是等待到某个时间点
cout << "end." << endl;
return 0;
}
6_8_生产者消费者.cpp
#include "hjcommon.hpp"
#include <queue>
#include <thread>
#include <mutex>
#include <memory>
#include <condition_variable>
using namespace std;
HJ_NS_USING
// 数据类
class Writer;
class Consumer;
class Data
{
public:
queue<int> m_dataQueue;
std::mutex m_mutex;
std::condition_variable m_cond;
int m_writerCount = 0;
int m_consumerCount = 0;
bool isAllWrited = false;
~Data() { cout << "~Data" << endl; }
void addWriter(unique_ptr<Writer> writer)
{
++m_writerCount;
m_writerQueue.push(std::move(writer));
}
void addComsumer(unique_ptr<Consumer> consumer)
{
++m_consumerCount;
m_comsumerQueue.push(std::move(consumer));
}
private:
queue<unique_ptr<Writer>> m_writerQueue;
queue<unique_ptr<Consumer>> m_comsumerQueue;
};
// 生产者
class Writer
{
public:
Writer(Data &data, int index) : m_data(data), m_index(index) {}
~Writer() { cout << "~Writer" << m_index << endl; }
void write()
{
int start = m_index*m_len;
int end = (m_index+1)*m_len;
for(int i=start; i<end; ++i)
{
unique_lock<mutex> uniqueLock(m_data.m_mutex);
m_data.m_dataQueue.push(i);
cout << "Writer" << m_index << " write : " << i << ", threadId=" << this_thread::get_id() << endl;
m_data.m_cond.notify_one(); // 这里用notify_all()效果一样的
}
cout << "Writer" << m_index << " finished" << ", threadId=" << this_thread::get_id() << endl;
// 结束所有consume线程死循环结
unique_lock<mutex> uniqueLock(m_data.m_mutex);
++m_wrtierCount;
if (m_wrtierCount==m_data.m_writerCount)
{
m_data.isAllWrited = true;
m_data.m_cond.notify_all(); // 这里必须用notify_all()了,因为有多个线程wait()了
}
}
private:
Data &m_data;
int m_index;
const int m_len = 100000;
static int m_wrtierCount;
};
int Writer::m_wrtierCount = 0;
// 消费者,一条数据只允许一个消费者消费
class Consumer
{
public:
Consumer(Data &data, int index) : m_data(data), m_index(index) {}
~Consumer() { cout << "~Consumer" << m_index << endl; }
void consume()
{
while(true)
{
unique_lock<mutex> uniqueLock(m_data.m_mutex);
m_data.m_cond.wait(uniqueLock, [this]{
return !m_data.m_dataQueue.empty() || m_data.isAllWrited;
});
if (!m_data.m_dataQueue.empty())
{
int data = m_data.m_dataQueue.front();
m_data.m_dataQueue.pop();
cout << "Consumer" << m_index << " consume : " << data << ", threadId=" << this_thread::get_id() << endl;
}
else
{
if (m_data.isAllWrited) break;
}
}
}
private:
Data &m_data;
int m_index;
};
int main_2_6_8_2(int argc, char *argv[])
{
// 生产者消费者模式 模拟(多个生产者线程,多个消费者线程)
Data data;
vector<thread> threadVec;
for (int i=0; i<10; ++i)
{
unique_ptr<Writer> p = make_unique<Writer>(data, i);
threadVec.push_back(thread(&Writer::write, p.get())); // 注意这里用的p.get(),传递的是对象指针,因为用的是unique_ptr,不要thread传参时发生拷贝构造
data.addWriter(std::move(p));
}
for (int i=0; i<5; ++i)
{
unique_ptr<Consumer> p = make_unique<Consumer>(data, i);
threadVec.push_back(thread(&Consumer::consume, p.get()));
data.addComsumer(std::move(p));
}
for (auto it=threadVec.begin(); it!=threadVec.end(); ++it)
{
it->join();
}
cout << "end." << endl;
return 0;
}