1、条件变量
当⼀个线程互斥地访问某个变量时,它可能发现在其它线程改变状态之前,它什么也做不了。
例如⼀个线程访问队列时,发现队列为空,它只能等待,直到其它线程将⼀个节点添加到队列中。这种情况就需要⽤到条件变量
2、同步概念与竞态条件
同步:在保证数据安全的前提下,让线程能够按照某种特定的顺序访问临界资源,从⽽有效避免饥饿问题,叫做同步
竞态条件:因为时序问题,⽽导致程序异常,我们称之为竞态条件。
条件变量函数
初始化
int pthread_cond_init(pthread_cond_t restrict cond , const pthread_condattr_t restrict attr);
参数: cond :要初始化的条件变量 attr : NULL
销毁
int pthread_cond_destroy(pthread_cond_t *cond)
等待
int pthread_cond_wait(pthread_cond_t restrict cond , pthread_mutex_t restrict mutex);
参数:
cond :要在这个条件变量上等待
mutex :互斥量
唤醒等待线程
int pthread_cond_broadcast(pthread_cond_t cond); 广播唤醒
int pthread_cond_signal(pthread_cond_t cond)
随机唤醒线程
测试代码
#include <iostream>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void *active(void *arg)
{
std::string name = static_cast<const char *>(arg);
while (true)
{
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cond, &mutex);
std::cout << name << " 活动..." << std::endl;
pthread_mutex_unlock(&mutex);
}
}
int main(void)
{
pthread_t t1, t2;
pthread_create(&t1, NULL, active, (void *)"thread-1");
pthread_create(&t2, NULL, active, (void *)"thread-2");
sleep(3); // 确保两个线程已经在运⾏
while (true)
{
// 对⽐测试
// pthread_cond_signal(&cond); // 唤醒⼀个线程
pthread_cond_broadcast(&cond); // 唤醒所有线程
sleep(1);
}
pthread_join(t1, NULL);
pthread_join(t2, NULL);
}
3、⽣产者消费者模型
基于BlockingQueue的⽣产者消费者模型
Mutex.hpp
#pragma once
#include<iostream>
#include<pthread.h>
class Mutex
{
public:
Mutex()
{
pthread_mutex_init(&_lock , nullptr);
}
void Lock()
{
pthread_mutex_lock(&_lock);
}
pthread_mutex_t *Ptr()
{
return &_lock;
}
void Unlock()
{
pthread_mutex_unlock(&_lock);
}
~Mutex()
{
pthread_mutex_destroy(&_lock);
}
private:
pthread_mutex_t _lock;
};
class LockGuard
{
public:
LockGuard(Mutex &lock):_lockref(lock)
{
_lockref.Lock();
}
~LockGuard()
{
_lockref.Unlock();
}
private:
Mutex &_lockref;
};
Cond.hpp
#ifndef __COND_HPP
#define _COND_HPP
#include<pthread.h>
#include"Mutex.hpp"
class Cond
{
public:
Cond()
{
pthread_cond_init(&_cond , nullptr);
}
void Wait(Mutex &mutex)
{
int n = pthread_cond_wait(&_cond , mutex.Ptr());
(void)n;
}
void Signal()
{
int n = pthread_cond_signal(&_cond);
(void)n;
}
void Broadcast()
{
int n = pthread_cond_broadcast(&_cond);
(void)n;
}
~Cond()
{
pthread_cond_destroy(&_cond);
}
private:
pthread_cond_t _cond;
};
#endif
BlockQueue
#ifndef __BLOCK_QUEUE_H
#define __BLOCK_QUEUE_H
#include <iostream>
#include <pthread.h>
#include <queue>
#include "Mutex.hpp"
#include "Cond.hpp"
const int defaultcap = 5;
template <typename T>
class BlockQueue
{
public:
BlockQueue(int cap = defaultcap) : _cap(cap)
{
sleep_consumer_num = 0;
sleep_productor_num = 0;
}
void Enqueue(T in)
{
{
LockGuard lockguard(_mutex);
while (_bp.size() == _cap)
{
sleep_productor_num++;
_productor_cond.Wait(_mutex);
sleep_productor_num--;
}
_bp.push(in);
if (sleep_consumer_num > 0)
_consumer_cond.Signal();
}
}
void Pop(T *out)
{
{
LockGuard lockguard(_mutex);
while (_bp.empty())
{
sleep_consumer_num++;
_consumer_cond.Wait(_mutex);
sleep_consumer_num--;
}
*out = _bp.front();
_bp.pop();
if (sleep_productor_num > 0)
_productor_cond.Signal();
}
}
~BlockQueue()
{
}
private:
std::queue<T> _bp;
int _cap;
Mutex _mutex;
Cond _consumer_cond;
Cond _productor_cond;
int sleep_productor_num;
int sleep_consumer_num;
};
#endif
Task.hpp
#ifndef __TASK_HPP
#define __TASK_HPP
#include<iostream>
#include<string>
#include<functional>
using task_t = std::function<void()>;
void Print()
{
std::cout<<"待处理任务" <<std::endl;
}
// class Task
// {
// public:
// Task(){}
// Task(int x , int y):_x(x) , _y(y)
// {}
// void Execute()
// {
// _result = _x + _y;
// }
// void operator()()
// {
// Execute();
// }
// std::string getResult()
// {
// return std::to_string(_x) + " + " + std::to_string(_y) + " = " +std::to_string(_result);
// }
// std::string Question()
// {
// return std::to_string(_x) + " + " + std::to_string(_y) + " =?";
// }
// ~Task()
// {}
// private:
// int _x;
// int _y;
// int _result;
// };
#endif
Thread.hpp
#pragma once
#include <iostream>
#include <string>
#include <functional>
#include <pthread.h>
namespace ThreadModule
{
static int gnumber = 1;
using callback_t = std::function<void()>;
enum class TSTATUS
{
THREAD_NEW,
THREAD_RUNNING,
THREAD_STOP
};
std::string Status2String(TSTATUS s)
{
switch (s)
{
case TSTATUS::THREAD_NEW:
return "THREAD_NEW";
case TSTATUS::THREAD_RUNNING:
return "THREAD_RUNNING";
case TSTATUS::THREAD_STOP:
return "THREAD_STOP";
default:
return "UNKNOWN";
}
}
std::string IsJoined(bool joinable)
{
return joinable ? "true" : "false";
}
class Thread
{
private:
void ToRunning()
{
_status = TSTATUS::THREAD_RUNNING;
}
void ToStop()
{
_status = TSTATUS::THREAD_STOP;
}
static void *ThreadRoutine(void *args)
{
Thread *self = static_cast<Thread *>(args);
pthread_setname_np(self->_tid, self->_name.c_str());
self->_cb();
self->ToStop();
return nullptr;
}
public:
Thread(callback_t cb)
: _tid(-1), _status(TSTATUS::THREAD_NEW), _joinable(true), _cb(cb), _result(nullptr)
{
_name = "New-Thread-" + std::to_string(gnumber++);
}
bool Start()
{
int n = pthread_create(&_tid, nullptr, ThreadRoutine, this);
if (n != 0)
return false;
ToRunning();
return true;
}
void Join()
{
if (_joinable)
{
int n = pthread_join(_tid, &_result);
if (n != 0)
{
std::cerr << "join error: " << n << std::endl;
return;
}
(void)_result;
_status = TSTATUS::THREAD_STOP;
}
else
{
std::cerr << "error, thread join status: " << IsJoined(_joinable) << std::endl;
}
}
void Die()
{
if (_status == TSTATUS::THREAD_RUNNING)
{
pthread_cancel(_tid);
_status = TSTATUS::THREAD_STOP;
}
}
void Detach()
{
if (_status == TSTATUS::THREAD_RUNNING && _joinable)
{
pthread_detach(_tid);
_joinable = false;
}
else
{
std::cerr << "detach " << _name << " failed" << std::endl;
}
}
void PrintInfo()
{
std::cout << "thread name : " << _name << std::endl;
std::cout << "thread _tid : " << _tid << std::endl;
std::cout << "thread _status : " << Status2String(_status) << std::endl;
std::cout << "thread _joinable : " << IsJoined(_joinable) << std::endl;
}
~Thread()
{
}
private:
std::string _name;
pthread_t _tid;
TSTATUS _status;
bool _joinable;
// 线程的任务,回调函数
callback_t _cb;
// 线程退出信息
void *_result;
};
}
Main.hpp
#include "BlockQueue.hpp"
#include "Thread.hpp"
#include "Task.hpp"
#include <memory>
#include <unistd.h>
#include <ctime>
#include <cstdlib>
int num = 1;
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
int GetNumber()
{
pthread_mutex_lock(&lock);
int number = num++;
pthread_mutex_unlock(&lock);
return number;
}
// void *ConsumerRoutine(void *args)
// {
// int number = GetNumber();
// std::string name = "Consumer-" + std::to_string(number);
// pthread_setname_np(pthread_self(), name.c_str());
// BlockQueue<int> *bp = static_cast<BlockQueue<int> *>(args);
// while (true)
// {
// sleep(1);
// int data = 0;
// bp->Pop(&data);
// std::cout << name << "消费: " << data << std::endl;
// }
// }
// void *ProdoutorRoutine(void *args)
// {
// int number = GetNumber();
// std::string name = "Prodoutor-" + std::to_string(number);
// pthread_setname_np(pthread_self(), name.c_str());
// BlockQueue<int> *bp = static_cast<BlockQueue<int> *>(args);
// int data = 10;
// while (true)
// {
// sleep(1);
// bp->Enqueue(data);
// std::cout << name << "生产: " << data++ << std::endl;
// }
// }
using namespace ThreadModule;
int main()
{
srand(time(nullptr) ^ getpid());
std::unique_ptr<BlockQueue<task_t>> bp = std::make_unique<BlockQueue<task_t>>();
Thread consumer([&bp]()
{
// int number = GetNumber();
// std::string name = "Consumer-" + std::to_string(number);
// pthread_setname_np(pthread_self() , name.c_str());
while(true)
{
sleep(1);
task_t t;
bp->Pop(&t);
// t.Execute();
t();
// std::cout<<"消费: " <<t.getResult() <<std::endl;
} });
Thread productor([&bp]()
{
// int number = GetNumber();
// std::string name = "Prodoutor-" + std::to_string(number);
// pthread_setname_np(pthread_self(), name.c_str());
while (true)
{
int x = rand() % 12 + 1;
usleep(rand()%1000);
int y = rand() % 6 + 1;
// Task t(x , y);
bp->Enqueue(Print);
// std::cout<< "生产: " <<t.Question() << std::endl;
} });
consumer.Start();
productor.Start();
consumer.Join();
productor.Join();
// BlockQueue<int> *bp = new BlockQueue<int>();
// pthread_t c , p;
// pthread_create(&p , nullptr , ProdoutorRoutine , bp);
// pthread_create(&c , nullptr , ConsumerRoutine , bp);
// pthread_join(c , nullptr);
// pthread_t c[3] , p[2];
// pthread_create(c , nullptr , ConsumerRoutine , bp);
// pthread_create(c+1 , nullptr , ConsumerRoutine , bp);
// pthread_create(c+2 , nullptr , ConsumerRoutine , bp);
// pthread_create(p , nullptr , ProdoutorRoutine , bp);
// pthread_create(p+1 , nullptr , ProdoutorRoutine , bp);
// pthread_join(c[0] , nullptr);
// pthread_join(c[1] , nullptr);
// pthread_join(c[2] , nullptr);
// pthread_join(p[0] , nullptr);
// pthread_join(p[1] , nullptr);
return 0;
}
makefile
cp_bq:Main.cc
g++ -o $@ $^ -std=c++14
.PHONY:clean
clean:
rm -f cp_bq
