前言
在写这边文章之前很长一段时间,几乎关于多线程对共享数据的保护都是使用互斥量
加非同步原语sleep
实现,导致性能的降低和cpu的利用率降低。
不要使用如下的代码块:
while(true){
if(!dataAvailable)
sleep(some_time);
else
consumeData();
}
后来发现一些问题: 如果sleep时间过长,会导致读写数据不实时(比如手机控制机器人运动),如果sleep的时间过短,导致任务内核分的时间片过多,导致CPU占用比较多,白白浪费了大量的性能(Q:但阻塞或中断性能这块是否也是cpu分时间片轮询呢?
)。
生产代码中线程的等待可分为两种,一种是等待资源的到来(比如一些复用IO,一般是应用态等待内核态可用,select/poll/eppl_wait等);一种是等着临界区可用时读写共享数据。
多线程对共享资源使用互斥量+sleep方式
- 是否推荐
线程I生产数据,线程II消费数据。
/*使用该接口替代sleep,原因为sleep的精度不高(涉及到調度延遲),因为设计进程调用等原因
我們稱爲該接口為優化后的定時器<<UNIX網絡變成卷1>> 6.3 P129
*/
void milliseconds_sleep(int mSec)
{
struct timeval tv;
tv.tv_sec=mSec/1000;
tv.tv_usec=(mSec%1000)*1000;
int err;
do{
err=select(0,NULL,NULL,NULL,&tv);
}while(err<0 && errno==EINTR);
}
//线程I
while(ture)
{
pthread_mutex_lock(&mutex);
//写 共享数据区
pthread_mutex_unlock(&mutex);
milliseconds_sleep(40);
}
线程II:
while(ture)
{
pthread_mutex_lock(&mutex);
//读 共享数据区
pthread_mutex_unlock(&mutex);
milliseconds_sleep(40);
}
互斥加条件变量(c实现)
线程I(生产者)
pthread_cond_signal(&cond);表示对共享区写数据(布尔值,比如判断共享区缓存的数量,布尔值改变了),触发软中断
while(true == is_run )
{
pthread_mutex_lock(&lock);
//对共享区写数据,改变布尔值,改变的时候通常用mutex保护
do somethings ...
pthread_mutex_unlock(&lock);
pthread_cond_signal(&cond);
}
线程II(消费者)
该端为wait端,条件变量只有一种正确使用方式:
1)必须与mutex一起使用,该布尔表达式的读写 需此mutex保护。
2)在mutex已上锁的时候才能调用wait();
3)把判断布尔条件和wait()放到while循环中,不能使用if去判断。
while(true = =is_run)
{
pthread_mutex_lock(&lock);//满足2)
while(!ringbuf.size()) //满足3)
{
pthread_cond_wait(&cond, &lock); //满足1)
}
if(false == is_run)
{
pthread_mutex_unlock(&lock);
break;
}
//读共享区数据,例如链表,队列等
do somethings...
pthread_mutex_unlock(&lock);
}
互斥加条件(C++实现)
使用:
#ifndef FUNC_H__
#define FUNC_H__
#include <thread>
#include <vector>
#include "muduo/base/Mutex.h"
#include "muduo/base/Condition.h"
#include "muduo/base/Thread.h"
#include "muduo/base/Types.h"
using namespace muduo;
class CCaptureMgr
{
public:
typedef struct TagCapture
{
char *data;
size_t siz;
}CAPTURE_S;
// enum {MEMERY_TYPE = 0, FILE_FD_TYPE = 1, CPLUSPLUS_THREAD_TYPE = 2 };
static CCaptureMgr *inst(){static CCaptureMgr cm; return &cm;}
int start();
int restart();
int stop();
protected:
int deleteSource(std::thread *);
int deleteSource(std::vector<CAPTURE_S> &);
void pushProc();
void popProc();
int put(const CAPTURE_S &);
int take();
private:
CCaptureMgr();
~CCaptureMgr();
mutable MutexLock mutex_ ;
Condition cond_ GUARDED_BY(mutex_);
std::thread *pPushHandle_;
std::thread *pPopHandle_;
bool bRun_;
std::vector<CAPTURE_S> captureDataList_;
};
#endif
#include <unistd.h>
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "function.hpp"
CCaptureMgr::CCaptureMgr():pPushHandle_(nullptr), pPopHandle_(nullptr),bRun_(true),
mutex_(), cond_(mutex_)
{}
CCaptureMgr::~CCaptureMgr()
{}
int CCaptureMgr::put(const CAPTURE_S &hs_)
{
MutexLockGuard lock(mutex_);
//if(hs_.data && hs_.siz)
{
captureDataList_.push_back(hs_);
cond_.notify();
}
}
int CCaptureMgr::take()
{
MutexLockGuard lock(mutex_);
while(captureDataList_.empty())
{
cond_.wait();
}
std::cout << "siz: " << captureDataList_.size() << std::endl;
}
void CCaptureMgr::pushProc()
{
while(bRun_)
{
static int recore = 0;
CAPTURE_S capture;
memset(&capture, 0, sizeof(capture));
capture.data = (char *)calloc(1, 1024);
capture.siz = strlen(capture.data);
put(capture);
sleep(1);
}
}
void CCaptureMgr::popProc()
{
while(bRun_)
{
take();
}
}
int CCaptureMgr::start()
{
int ret = 0;
if(nullptr == pPopHandle_)
{
pPopHandle_ = new std::thread(&CCaptureMgr::popProc, this);
if(!pPopHandle_)
{
std::cout << "创建POP线程失败! " << std::endl;
}
}
if(nullptr == pPushHandle_)
{
pPushHandle_ = new std::thread(&CCaptureMgr::pushProc, this);
if(!pPushHandle_)
{
std::cout << "创建PUSH线程失败! " << std::endl;
}
}
return 0;
}
int CCaptureMgr::restart()
{
stop();
start();
}
int CCaptureMgr::deleteSource(std::thread *p)
{
if(p)
{
p->join(); delete p; p = 0;
}
}
int CCaptureMgr::deleteSource(std::vector<CAPTURE_S> &hs_)
{
if(hs_.size())
{
auto it = hs_.begin();
for(it; it != hs_.end(); ++it)
{
if(it->data)
{
free(it->data); it->data = 0;
}
}
}
}
int CCaptureMgr::stop()
{
deleteSource(pPushHandle_);
deleteSource(pPopHandle_);
return 0;
}