个人主页:Lei宝啊
愿所有美好如期而遇
前言:线程互斥
前面我们谈完了线程互斥,但是有一个问题,所有线程去竞争一个锁,如果有一个线程竞争能力比较强,他一直能够抢到锁,对不同的场景,这也许没问题,只是不合理,但也许就是错的,所以我们需要线程同步,让这些进程能够按照一定的顺序去执行。
条件变量
我们通俗的将,就是在满足我们写的某个条件时,调用一个函数使线程在这个条件变量下等待并释放锁,在线程被唤醒并竞争到锁时,从等待位置再继续执行代码。
定义一个条件变量
仍然是两种方式,同互斥一般,全局静态定义的条件变量,后续不需要destroy,局部定义的需要调用这个函数。
pthread_cond_wait
第一个参数传条件变量,第二个参数传申请到的锁。
这个函数的意思是:使一个线程在cond条件变量下阻塞等待,并释放他所申请的mutex锁,直到线程被唤醒时,线程再去申请锁,如果申请成功,那么再执行这个函数下面的代码。
用法举例:
唤醒线程
broadcast唤醒所有在*cond条件变量下等待的线程,signal唤醒*cond队列首部的线程。
生产消费模型
什么是生产消费模型?就是维护多线程之间的同步和互斥,总结为三种关系,两种角色,以及一个交易场所,交易场所:临时保存数据的内存空间,即某种数据结构。
生产消费模型+条件变量:基于BlockingQueue的生产者消费者模型
在多线程编程中阻塞队列(Blocking Queue)是一种常用于实现生产者和消费者模型的数据结构。其与普通的队列区别在于,当队列为空时,从队列获取元素的操作将会被阻塞,直到队列中被放入了元素;当队列满时,往队列里存放元素的操作也会被阻塞,直到有元素被从队列中取出(以上的操作都是基于不同的线程来说的,线程在对阻塞队列进程操作时会被阻塞)。
cpp
#pragma once
#include <pthread.h>
#include <iostream>
#include <vector>
#include <string>
#include <cstdlib>
#include <cstring>
#include <functional>
#include <unistd.h>
#include "blockqueue.hpp"
using namespace std;
template<typename T>
using func_t = std::function<void(T&)>;
// typedef std::function<void(const T&)> func_t;
#define DataType int
#define TDataBlock BlockQueue<DataType>
#define TYPE Thread<BlockQueue<DataType>>
template<typename T>
class Thread
{
public:
void Excute()
{
_func(_data);
}
public:
Thread(func_t<T> func, T &data, const std::string &name="none-name")
: _func(func), _data(data), _threadname(name), _stop(true)
{}
static void *threadroutine(void *args) // 类成员函数,形参是有this指针的!!
{
Thread<T> *self = static_cast<Thread<T> *>(args);
self->Excute();
return nullptr;
}
bool Start()
{
int n = pthread_create(&_tid, nullptr, threadroutine, this);
if(!n)
{
_stop = false;
return true;
}
else
{
return false;
}
}
void Detach()
{
if(!_stop)
{
pthread_detach(_tid);
}
}
void Join()
{
if(!_stop)
{
pthread_join(_tid, nullptr);
}
}
std::string name()
{
return _threadname;
}
void Stop()
{
_stop = true;
}
~Thread() {}
private:
pthread_t _tid;
std::string _threadname;
T &_data; // 为了让所有的线程访问同一个全局变量
func_t<T> _func;
bool _stop;
};
cpp
#include "blockqueue.hpp"
#include "Thread_mutex.hpp"
using namespace std;
void funcofmain(TDataBlock& bd)
{
int cnt1 = 0;
while(true)
{
bd.Push(cnt1);
cout << "--funcofmain" << ">>生产in: " << cnt1 << endl;
cnt1++;
}
}
void funcofless(TDataBlock& bd)
{
int cnt2 = 0;
while(true)
{
bd.Pop(cnt2);
cout << "--funcofless" << ">>消费out: " << cnt2 << endl;
sleep(1);
}
}
void CommFunc(vector<TYPE>& vtb, int pthreadnum,
function<void(TDataBlock&)> func, string what, BlockQueue<DataType>& bq)
{
for(int i=0; i<pthreadnum; i++)
{
string thread_name = what + "-thread-" + to_string(i+1);
vtb.emplace_back(func, bq);
vtb.back().Start();
}
}
void MainThread(vector<TYPE>& vtb, int pthreadnum, BlockQueue<DataType>& bq)
{
CommFunc(vtb, pthreadnum, funcofmain, "main", bq);
}
void LessThread(vector<TYPE>& vtb, int pthreadnum, BlockQueue<DataType>& bq)
{
CommFunc(vtb, pthreadnum, funcofless, "less", bq);
}
void WaitThread(vector<TYPE>& vtb)
{
for(auto &e: vtb) e.Join();
}
int main()
{
int a = 10;
vector<TYPE> vtb;
BlockQueue<DataType>* bq = new BlockQueue<DataType>(a);
MainThread(vtb, 1, *bq);
LessThread(vtb, 3, *bq);
WaitThread(vtb);
return 0;
}
cpp
#pragma once
#include "Thread_mutex.hpp"
#include <queue>
using namespace std;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t productor = PTHREAD_COND_INITIALIZER;
pthread_cond_t consumer = PTHREAD_COND_INITIALIZER;
template<class T>
class BlockQueue
{
public:
BlockQueue(){}
BlockQueue(int data)
:_data(data)
{}
bool IsFull()
{
return que.size() == _data;
}
bool IsNull()
{
return que.empty();
}
void Push(T& in)
{
pthread_mutex_lock(&mutex);
while(IsFull())
{
pwait++;
pthread_cond_wait(&productor, &mutex);
pwait--;
}
que.push(in);
if(cwait > 0)
pthread_cond_signal(&consumer);
pthread_mutex_unlock(&mutex);
}
void Pop(T& out)
{
pthread_mutex_lock(&mutex);
while(IsNull())
{
cwait++;
pthread_cond_wait(&consumer, &mutex);
cwait--;
}
out = que.front();
que.pop();
if(pwait > 0)
pthread_cond_signal(&productor);
pthread_mutex_unlock(&mutex);
}
private:
int _data;
queue<T> que;
int pwait = 0;
int cwait = 0;
};