题目要求:1)假设有一个Counter类,包含一个id和计数次数mNumIters,支持多线程(互斥体)完成计数任务;2)设计一个发送者和消费者模式的数据传输模型;3)支持多线程加法运算,实现每个Counter自加后结果不变;4)考虑设计一个日志类,多线程读写数据,写入数据后存储到log.txt文件中。
分析:
1)定义基于mutex的多线程任务,mutex支持lock_gurad和unique_lock等加锁方式,考虑重写类Counter的operator(),实现参数传递;
2)定义基于条件变量的多线程任务,condition_variable支持wait()和notify_all()/notify_one()等方法根据条件完成对已加锁的mutex的操作;
3)定义原子性counter,每个线程完成各自的加法运算值递增到counter中,保持数据同步;
4)Logger类需要考虑数据存储结构,此处定义为队列queue<string>,通过互斥体保护队列数据的访问,考虑到可能存在后台程序崩溃,需要设计相应的析构函数和对应的是否退出的原子性变量mExit
5) 标准库多线程的编译指令:g++ -Wall -pthread -g -o desApp source.cpp
代码实现:
1:互斥体的基本方法
/*************************************************************************
> File Name: mutexDemo.cpp
> Author: xxx
> Mail: email@xx.com
> Created Time: 2021年12月08日 星期三 15时51分30秒
************************************************************************/
#include<iostream>
#include<string>
#include<vector>
#include<map>
#include<algorithm>
#include<climits>
#include<mutex>
#include<chrono>
#include<thread>
using namespace std;
class Counter{
public:
Counter(int id, int numIters):mId(id), mNumIters(numIters){
}
// lock_guard
/*
void operator()() const{
for(int i = 0; i < mNumIters; ++i){
lock_guard<mutex> lock(mMutex);
cout << "Counter " << mId << " has value " << i << endl;
}
}*/
// unique_lock
void operator()() const{
for(int i = 0; i < mNumIters; ++i){
unique_lock<timed_mutex> lock(mTimeMutex, 200ms);
if(lock){
cout <<"Counter " << mId << " has value " << i << endl;
}
// else lock 200ms
}
}
private:
int mId;
int mNumIters;
// static mutex mMutex for lock_guard;
static timed_mutex mTimeMutex;
};
// mutex Counter::mMutex;
timed_mutex Counter::mTimeMutex;
int main(){
thread t1{Counter{1, 30}};
Counter c(2, 15);
thread t2(c);
thread t3(Counter(3, 10));
t1.join();
t2.join();
t3.join();
return 0;
}
2、条件变量的使用方法(使用条件变量和互斥体的区别)
/*************************************************************************
> File Name: condVarDemo.cpp
> Author: xxx
> Mail: email@xx.com
> Created Time: 2021年12月08日 星期三 16时14分09秒
************************************************************************/
#include<iostream>
#include<string>
#include<vector>
#include<map>
#include<algorithm>
#include<climits>
#include<thread>
#include<mutex>
#include<deque>
#include<chrono>
#include<condition_variable>
using namespace std;
std::deque<int> q; // global queue
std::mutex mMutex; // global mutex
std::condition_variable mCondVar; //global condition variable
void provider(){
int count = 10;
while(count > 0){
unique_lock<mutex> lock(mMutex);
q.push_front(count);
lock.unlock();
std::this_thread::sleep_for(std::chrono::seconds(1));
count--;
}
}
void consumer(){
int data = 0;
while(data != 1){
unique_lock<mutex> lock(mMutex);
if(!q.empty()){
data = q.back();
q.pop_back();
lock.unlock();
cout << "consumter get data from provider: " << data << endl;
}else{
lock.unlock();
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
}
}
void providerII(){
int count = 10;
while(count > 0){
unique_lock<mutex> lock(mMutex);
q.push_front(count);
lock.unlock();
// add condvar notification
mCondVar.notify_one();
std::this_thread::sleep_for(std::chrono::seconds(1));
count--;
}
}
void consumerII(){
int data = 0;
while(data != 1){
unique_lock<mutex> lock(mMutex);
/*while(q.empty()){ // note for multitime to check queue data
mCondVar.wait(lock);
} for better understand, use next line to replace*/
mCondVar.wait(lock, [](){return !q.empty();});
data = q.back();
q.pop_back();
lock.unlock();
cout << "consumer get data from provider: " << data << endl;
}
}
int main(){
thread t1(providerII);
thread t2(consumerII);
t1.join();
t2.join();
return 0;
}
3:原子操作(线程中函数的传入是复制形式传入,转换为引用传入需要使用std::ref(var))
/*************************************************************************
> File Name: atomicDemo.cpp
> Author: xxx
> Mail: email@xx.com
> Created Time: 2021年12月08日 星期三 14时40分34秒
************************************************************************/
#include<iostream>
#include<string>
#include<vector>
#include<map>
#include<algorithm>
#include<climits>
#include<thread>
#include<atomic>
#include<chrono>
using namespace std;
void func(int& counter){
for(int i = 0; i < 100; ++i){
++counter;
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
}
void funcII(std::atomic<int>& counter){
int res = 0; // to improve performence
for(int i = 0; i < 100; ++i){
++res;
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
counter += res;
}
int main(){
// int counter = 0;
std::atomic<int> counter(0);
std::vector<std::thread> threads;
for(int i = 0; i < 10; ++i){
threads.push_back(std::thread{funcII, std::ref(counter)});
}
for(auto& t : threads){
t.join();
}
cout << "Result = " << counter << endl;
return 0;
}
4:Logger类实现方法
//logger.h
#ifndef LOGGER_H_
#define LOGGER_H_
#include<queue>
#include<thread>
#include<string>
#include<mutex>
#include<condition_variable>
#include<atomic>
class Logger{
public:
Logger();
Logger(const Logger& src) = delete;
Logger& operator=(const Logger& rhs) = delete;
void log(const std::string& entry);
virtual ~Logger();
private:
std::atomic<bool> mExit;
// Mutex and condition variable to protect access to the queue
std::mutex mMutex;
std::condition_variable mCondVar;
std::queue<std::string> mQueue;
std::thread mThread;
void processEntries();
};
#endif
//logger.cpp
#include<fstream>
#include<iostream>
#include"logger.h"
using namespace std;
Logger::Logger(): mExit(false){
mThread = thread{&Logger::processEntries, this};
}
Logger::~Logger(){
{
unique_lock<mutex> lock(mMutex);
mExit = true;
mCondVar.notify_all();
}
mThread.join();
}
void Logger::log(const std::string& entry){
unique_lock<mutex> lock(mMutex);
mQueue.push(entry);
mCondVar.notify_all();
}
void Logger::processEntries(){
ofstream ofs("log.txt");
if(ofs.fail()){
cout << "Fail to open logfile. " << endl;
}
unique_lock<mutex> lock(mMutex);
while(true){
if(!mExit){
mCondVar.wait(lock);
}
lock.unlock();
while(true){
lock.lock();
if(mQueue.empty()){
break;
}else{
ofs << mQueue.front() << endl;
mQueue.pop();
}
lock.unlock();
}
if(mExit){
break;
}
}
}
// main.cpp
#include<iostream>
#include<sstream>
#include<thread>
#include<vector>
#include"logger.h"
using namespace std;
void logSomeMessages(int id, Logger& logger){
for(int i = 0; i < 10; i++){
stringstream ss;
ss << "Log entry " << i << " from thread " << id;
logger.log(ss.str());
}
}
int main(){
Logger logger;
vector<thread> threads;
for(int i = 0; i < 10; i++){
threads.emplace_back(logSomeMessages, i, ref(logger));
}
for(auto& t : threads){
t.join();
}
return 0;
}