目录
生产者消费者模型
条件变量&&互斥锁(阻塞队列)
makefile
CC=g++ FLAGS=-std=c++11 LD=-lpthread bin=blockQueue src=BlockQueueTest.cc $(bin):$(src) $(CC) -o $@ $^ $(LD) $(FLAGS) .PHONY:clean clean: rm -f $(bin)
Task.hpp
#pragma once #include <iostream> #include <string> class Task { public: Task() : elemOne_(0), elemTwo_(0), operator_('0') { } Task(int one, int two, char op) : elemOne_(one), elemTwo_(two), operator_(op) { } int operator() () { return run(); } int run() { int result = 0; switch (operator_) { case '+': result = elemOne_ + elemTwo_; break; case '-': result = elemOne_ - elemTwo_; break; case '*': result = elemOne_ * elemTwo_; break; case '/': { if (elemTwo_ == 0) { std::cout << "div zero, abort" << std::endl; result = -1; } else { result = elemOne_ / elemTwo_; } } break; case '%': { if (elemTwo_ == 0) { std::cout << "mod zero, abort" << std::endl; result = -1; } else { result = elemOne_ % elemTwo_; } } break; default: std::cout << "非法操作: " << operator_ << std::endl; break; } return result; } int get(int *e1, int *e2, char *op) { *e1 = elemOne_; *e2 = elemTwo_; *op = operator_; } private: int elemOne_; int elemTwo_; char operator_; };
BlockQueue.hpp
#pragma once #include <iostream> #include <queue> #include <cstdlib> #include <unistd.h> #include <pthread.h> using namespace std; const uint32_t gDefaultCap = 5; template <class T> class BlockQueue { public: BlockQueue(uint32_t cap = gDefaultCap) : cap_(cap) { pthread_mutex_init(&mutex_, nullptr); pthread_cond_init(&conCond_, nullptr); pthread_cond_init(&proCond_, nullptr); } ~BlockQueue() { pthread_mutex_destroy(&mutex_); pthread_cond_destroy(&conCond_); pthread_cond_destroy(&proCond_); } public: //生产接口 void push(const T &in) // const &: 纯输入 { // 加锁 // 判断->是否适合生产->bq是否为满->程序员视角的条件->1. 满(不生产) 2. 不满(生产) // if(满) 不生产,休眠 // else if(不满) 生产,唤醒消费者 // 解锁 lockQueue(); while (isFull()) // ifFull就是我们在临界区中设定的条件 { // before: 当我等待的时候,会自动释放mutex_ proBlockWait(); //阻塞等待,等待被唤醒。 被唤醒 != 条件被满足(概率虽然很小),被唤醒 && 条件被满足 // after: 当我醒来的时候,我是在临界区里醒来的!! } // 条件满足,可以生产 pushCore(in); //生产完成 // wakeupCon(); // 唤醒消费者 unlockQueue(); wakeupCon(); // 唤醒消费者 } //消费接口 T pop() { // 加锁 // 判断->是否适合消费->bq是否为空->程序员视角的条件->1. 空(不消费) 2. 有(消费) // if(空) 不消费,休眠 // else if(有) 消费,唤醒生产者 // 解锁 lockQueue(); while (isEmpty()) { conBlockwait(); //阻塞等待,等待被唤醒,? } // 条件满足,可以消费 T tmp = popCore(); unlockQueue(); wakeupPro(); // 唤醒生产者 return tmp; } private: void lockQueue() { pthread_mutex_lock(&mutex_); } void unlockQueue() { pthread_mutex_unlock(&mutex_); } bool isEmpty() { return bq_.empty(); } bool isFull() { return bq_.size() == cap_; } void proBlockWait() // 生产者一定是在临界区中的! { // 1. 在阻塞线程的时候,会自动释放mutex_锁 pthread_cond_wait(&proCond_, &mutex_); } void conBlockwait() //阻塞等待,等待被唤醒 { // 1. 在阻塞线程的时候,会自动释放mutex_锁 pthread_cond_wait(&conCond_, &mutex_); // 2. 当阻塞结束,返回的时候,pthread_cond_wait,会自动帮你重新获得mutex_,然后才返回 // 为什么我们上节课,写的代码,批量退出线程的时候,发现无法退出? } void wakeupPro() // 唤醒生产者 { pthread_cond_signal(&proCond_); } void wakeupCon() // 唤醒消费者 { pthread_cond_signal(&conCond_); } void pushCore(const T &in) { bq_.push(in); //生产完成 } T popCore() { T tmp = bq_.front(); bq_.pop(); return tmp; } private: uint32_t cap_; //容量 queue<T> bq_; // blockqueue pthread_mutex_t mutex_; //保护阻塞队列的互斥锁 pthread_cond_t conCond_; // 让消费者等待的条件变量 pthread_cond_t proCond_; // 让生产者等待的条件变量 };
BlockQueueTest.cc
#include "Task.hpp" #include "BlockQueue.hpp" #include <ctime> const std::string ops = "+-*/%"; // 并发,并不是在临界区中并发(一般),而是生产前(before blockqueue),消费后(after blockqueue)对应的并发 void *consumer(void *args) { BlockQueue<Task> *bqp = static_cast<BlockQueue<Task> *>(args); while (true) { Task t = bqp->pop(); // 消费任务 int result = t(); //处理任务 --- 任务也是要花时间的! int one, two; char op; t.get(&one, &two, &op); cout << "consumer[" << pthread_self() << "] " << (unsigned long)time(nullptr) << " 消费了一个任务: " << one << op << two << "=" << result << endl; } } void *productor(void *args) { BlockQueue<Task> *bqp = static_cast<BlockQueue<Task> *>(args); while (true) { // 1. 制作任务 --- 要不要花时间?? -- 网络,磁盘,用户 int one = rand() % 50; int two = rand() % 20; char op = ops[rand() % ops.size()]; Task t(one, two, op); // 2. 生产任务 bqp->push(t); cout << "producter[" << pthread_self() << "] " << (unsigned long)time(nullptr) << " 生产了一个任务: " << one << op << two << "=?" << endl; sleep(1); } } int main() { srand((unsigned long)time(nullptr) ^ getpid()); // 定义一个阻塞队列 // 创建两个线程,productor, consumer // productor ----- consumer // BlockQueue<int> bq; // bq.push(10); // int a = bq.pop(); // cout << a << endl; // 既然可以使用int类型的数据,我们也可以使用自己封装的类型,包括任务 // BlockQueue<int> bq; BlockQueue<Task> bq; pthread_t c, p; pthread_create(&c, nullptr, consumer, &bq); pthread_create(&p, nullptr, productor, &bq); pthread_join(c, nullptr); pthread_join(p, nullptr); return 0; }
信号量&&互斥锁(环形队列)
makefile
CC=g++ FLAGS=-std=c++11 LD=-lpthread bin=ringQueue src=RingQueueTest.cc $(bin):$(src) $(CC) -o $@ $^ $(LD) $(FLAGS) .PHONY:clean clean: rm -f $(bin)
RingQueue.hpp
#pragma once #include <iostream> #include <vector> #include <string> #include <semaphore.h> using namespace std; const int gCap = 10; template <class T> class RingQueue { public: RingQueue(int cap = gCap): ringqueue_(cap), pIndex_(0), cIndex_(0) { // 生产 sem_init(&roomSem_, 0, ringqueue_.size()); // 消费 sem_init(&dataSem_, 0, 0); pthread_mutex_init(&pmutex_ ,nullptr); pthread_mutex_init(&cmutex_ ,nullptr); } // 生产 void push(const T &in) { sem_wait(&roomSem_); //无法被多次的申请 pthread_mutex_lock(&pmutex_); ringqueue_[pIndex_] = in; //生产的过程 pIndex_++; // 写入位置后移 pIndex_ %= ringqueue_.size(); // 更新下标,保证环形特征 pthread_mutex_unlock(&pmutex_); sem_post(&dataSem_); } // 消费 T pop() { sem_wait(&dataSem_); pthread_mutex_lock(&cmutex_); T temp = ringqueue_[cIndex_]; cIndex_++; cIndex_ %= ringqueue_.size();// 更新下标,保证环形特征 pthread_mutex_unlock(&cmutex_); sem_post(&roomSem_); return temp; } ~RingQueue() { sem_destroy(&roomSem_); sem_destroy(&dataSem_); pthread_mutex_destroy(&pmutex_); pthread_mutex_destroy(&cmutex_); } private: vector<T> ringqueue_; // 环形队列 sem_t roomSem_; // 衡量空间计数器,productor sem_t dataSem_; // 衡量数据计数器,consumer uint32_t pIndex_; // 当前生产者写入的位置, 如果是多线程,pIndex_也是临界资源 uint32_t cIndex_; // 当前消费者读取的位置,如果是多线程,cIndex_也是临界资源 pthread_mutex_t pmutex_; pthread_mutex_t cmutex_; };
RingQueueTest.cc
#include "RingQueue.hpp" #include <ctime> #include <unistd.h> // 我们是单生产者,单消费者 // 多生产者,多消费者??代码怎么改? // 为什么呢???多生产者,多消费者? // 不要只关心把数据或者任务,从ringqueue 放拿的过程,获取数据或者任务,处理数据或者任务,也是需要花时间的! void *productor(void *args) { RingQueue<int> *rqp = static_cast<RingQueue<int> *>(args); while(true) { int data = rand()%10; rqp->push(data); cout << "pthread[" << pthread_self() << "]" << " 生产了一个数据: " << data << endl; sleep(1); } } void *consumer(void *args) { RingQueue<int> *rqp = static_cast<RingQueue<int> *>(args); while(true) { //sleep(10); int data = rqp->pop(); cout << "pthread[" << pthread_self() << "]" << " 消费了一个数据: " << data << endl; } } int main() { srand((unsigned long)time(nullptr)^getpid()); RingQueue<int> rq; pthread_t c1,c2,c3, p1,p2,p3; pthread_create(&p1, nullptr, productor, &rq); pthread_create(&p2, nullptr, productor, &rq); pthread_create(&p3, nullptr, productor, &rq); pthread_create(&c1, nullptr, consumer, &rq); pthread_create(&c2, nullptr, consumer, &rq); pthread_create(&c3, nullptr, consumer, &rq); pthread_join(c1, nullptr); pthread_join(c2, nullptr); pthread_join(c3, nullptr); pthread_join(p1, nullptr); pthread_join(p2, nullptr); pthread_join(p3, nullptr); return 0; }
线程池(封装任务)
makefile
CC=g++ FLAGS=-std=c++11 LD=-lpthread bin=threadpool src=ThreadPoolTest.cc $(bin):$(src) $(CC) -o $@ $^ $(LD) $(FLAGS) .PHONY:clean clean: rm -f $(bin)
Lock.hpp(RAII)
#pragma once #include <iostream> #include <pthread.h> class Mutex { public: Mutex() { pthread_mutex_init(&lock_, nullptr); } void lock() { pthread_mutex_lock(&lock_); } void unlock() { pthread_mutex_unlock(&lock_); } ~Mutex() { pthread_mutex_destroy(&lock_); } private: pthread_mutex_t lock_; }; class LockGuard { public: LockGuard(Mutex *mutex) : mutex_(mutex) { mutex_->lock(); std::cout << "加锁成功..." << std::endl; } ~LockGuard() { mutex_->unlock(); std::cout << "解锁成功...." << std::endl; } private: Mutex *mutex_; };
Log.hpp
#pragma once #include <iostream> #include <ctime> #include <pthread.h> std::ostream &Log() { std::cout << "Fot Debug |" << " timestamp: " << (uint64_t)time(nullptr) << " | " << " Thread[" << pthread_self() << "] | "; return std::cout; }
Task.hpp
#pragma once #include <iostream> #include <string> class Task { public: Task() : elemOne_(0), elemTwo_(0), operator_('0') { } Task(int one, int two, char op) : elemOne_(one), elemTwo_(two), operator_(op) { } int operator() () { return run(); } int run() { int result = 0; switch (operator_) { case '+': result = elemOne_ + elemTwo_; break; case '-': result = elemOne_ - elemTwo_; break; case '*': result = elemOne_ * elemTwo_; break; case '/': { if (elemTwo_ == 0) { std::cout << "div zero, abort" << std::endl; result = -1; } else { result = elemOne_ / elemTwo_; } } break; case '%': { if (elemTwo_ == 0) { std::cout << "mod zero, abort" << std::endl; result = -1; } else { result = elemOne_ % elemTwo_; } } break; default: std::cout << "非法操作: " << operator_ << std::endl; break; } return result; } int get(int *e1, int *e2, char *op) { *e1 = elemOne_; *e2 = elemTwo_; *op = operator_; } private: int elemOne_; int elemTwo_; char operator_; };
ThreadPool.hpp
#pragma once #include <iostream> #include <cassert> #include <queue> #include <memory> #include <cstdlib> #include <pthread.h> #include <unistd.h> #include <sys/prctl.h> #include "Log.hpp" #include "Lock.hpp" using namespace std; int gThreadNum = 5; template <class T> class ThreadPool { private: ThreadPool(int threadNum = gThreadNum) : threadNum_(threadNum), isStart_(false) { assert(threadNum_ > 0); pthread_mutex_init(&mutex_, nullptr); pthread_cond_init(&cond_, nullptr); } ThreadPool(const ThreadPool<T> &) = delete; void operator=(const ThreadPool<T>&) = delete; public: static ThreadPool<T> *getInstance() { static Mutex mutex; if (nullptr == instance) //仅仅是过滤重复的判断 { LockGuard lockguard(&mutex); //进入代码块,加锁。退出代码块,自动解锁 if (nullptr == instance) { instance = new ThreadPool<T>(); } } return instance; } //类内成员, 成员函数,都有默认参数this static void *threadRoutine(void *args) { pthread_detach(pthread_self()); ThreadPool<T> *tp = static_cast<ThreadPool<T> *>(args); prctl(PR_SET_NAME, "follower"); while (1) { tp->lockQueue(); while (!tp->haveTask()) { tp->waitForTask(); } //这个任务就被拿到了线程的上下文中 T t = tp->pop(); tp->unlockQueue(); // for debug int one, two; char oper; t.get(&one, &two, &oper); //规定,所有的任务都必须有一个run方法 Log() << "新线程完成计算任务: " << one << oper << two << "=" << t.run() << "\n"; } } void start() { assert(!isStart_); for (int i = 0; i < threadNum_; i++) { pthread_t temp; pthread_create(&temp, nullptr, threadRoutine, this); } isStart_ = true; } void push(const T &in) { lockQueue(); taskQueue_.push(in); choiceThreadForHandler(); unlockQueue(); } ~ThreadPool() { pthread_mutex_destroy(&mutex_); pthread_cond_destroy(&cond_); } private: void lockQueue() { pthread_mutex_lock(&mutex_); } void unlockQueue() { pthread_mutex_unlock(&mutex_); } bool haveTask() { return !taskQueue_.empty(); } void waitForTask() { pthread_cond_wait(&cond_, &mutex_); } void choiceThreadForHandler() { pthread_cond_signal(&cond_); } T pop() { T temp = taskQueue_.front(); taskQueue_.pop(); return temp; } private: bool isStart_; int threadNum_; queue<T> taskQueue_; pthread_mutex_t mutex_; pthread_cond_t cond_; static ThreadPool<T> *instance; // const static int a = 100; }; template <class T> ThreadPool<T> *ThreadPool<T>::instance = nullptr;
ThreadPoolTest.cc
#include "ThreadPool.hpp" #include "Task.hpp" #include <ctime> #include <thread> // 如何对一个线程进行封装, 线程需要一个回调函数,支持lambda // class tread{ // }; int main() { prctl(PR_SET_NAME, "master"); const string operators = "+/*/%"; // unique_ptr<ThreadPool<Task> > tp(new ThreadPool<Task>()); unique_ptr<ThreadPool<Task> > tp(ThreadPool<Task>::getInstance()); tp->start(); srand((unsigned long)time(nullptr) ^ getpid() ^ pthread_self()); // 派发任务的线程 while(true) { int one = rand()%50; int two = rand()%10; char oper = operators[rand()%operators.size()]; Log() << "主线程派发计算任务: " << one << oper << two << "=?" << "\n"; Task t(one, two, oper); tp->push(t); sleep(1); } }