C++多线程管理实操

多线程开发在实际工作中十分重要,在开发C++程序时,一般在吞吐量、并发、实时性上有较高的要求。因此,本文旨在讲解多线程的基本概念,以及利用C++编写一种生产者-消费者模型的多线程实例。

基本概念

通常,并发编程有两种模式,分别是多进程并发和多线程并发。多进程并发指的是开辟多个独立且只包含一个线程的进程,进程间互相通信,共同完成一个任务。但是,由于操作系统对进程提供了大量的保护机制,不同进程之间的数据不能互相修改,一方面,体现了安全性,另一方面,不可避免存在一定问题,无论是运行效率还是内存开销,都有待提升。

在当前的多核时代,线程能达到真正的并行,使得多线程并发由此诞生。由于线程是轻量级的进程,线程不独立占用资源,而是依赖于父进程。同一个进程中的多个线程共享同一块资源,线程间能高效地进行数据共享和通信。

信号量sem_t

信号量的数据类型为结构sem_t,本质是一个长整型的数;

初始化方法:extern int sem_init __P ((sem_t *__sem, int __pshared, unsigned int __value));

sem为指向信号量结构的一个指针;

pshared不为0时此信号量在进程间共享,否则只能为当前进程的所有线程共享;

value为信号量的初始值;

函数sem_post( sem_t *sem )用来增加信号量的值当有线程阻塞在这个信号量上时,调用这个函数会使其中的一个线程不再阻塞,选择机制同样是由线程的调度策略决定的。

函数sem_wait( sem_t *sem )被用来阻塞当前线程直到信号量sem的值大于0,解除阻塞后将sem的值减一,表明公共资源经使用后减少。

互斥器Mutex

多个线程访问同一资源时,为了保证数据的一致性,最简单的方式就是使用 互斥锁mutex。

总结下Mutex的操作步骤,分为以下几个功能:

1. 定义一个全局互斥器Mutex; 

2. 锁住互斥器Mutex:获得一个Mutex的拥有权,其他线程只能等待;

3. 释放互斥器Mutex,使得后一个等待的线程能够拥有它并得以获得资源;

生产者-消费者实例

#ifndef TASK_INTERFACE_H
#define TASK_INTERFACE_H

#include <string>
#include <vector>

using std::string;
using std::vector;

/**
 * 多线程任务自定义接口
 */
class TaskInterfece
{
public:
    TaskInterfece(){}
    virtual void run(vector<string>& dataBuffer) = 0;
};


#endif //TASK_INTERFACE_H
#ifndef MY_TASK_H
#define MY_TASK_H

#include <iostream>
#include "TaskInterfece.h"
using namespace std;

class MyTask : public TaskInterfece
{
public:
    MyTask(){}
    // do your thing
    virtual void run(vector<string>& dataBuffer)
    {
        cout << "==========\n";
        for(int i = 0; i < dataBuffer.size(); ++i)
        {
            cout << dataBuffer[i] << endl;
        }
        cout << "**********\n";
    }
};


#endif //MY_TASK_H
#ifndef FRAME_H
#define FRAME_H

#include <string>
#include <vector>
#include <utility>
#include <unordered_map>
#include <thread>
#include <mutex>
#include <queue>
#include <condition_variable>
#include <semaphore.h>
#include <iostream>
#include "TaskInterfece.h"

using namespace std;

/**
 * 多线程框架
 */
class Frame
{
public:
    Frame(){}
    bool init(TaskInterfece& task, int t_num, int buf_size = 5000, int log_num = 200000);
    void run();

private:
    int threadNum;
    TaskInterfece* pTask;
    mutex bufMtx;
    sem_t semPro, semCon;
    queue<string> buffer;
    vector<thread> threadVec;
    int bufSize;
    int logNum;
    void proThread();
    void conThread();
};


#endif //FRAME_H
#include "Frame.h"

bool Frame::init(TaskInterfece& task, int t_num, int buf_size, int log_num)
{
    pTask = &task;
    threadNum = t_num;
    bufSize = buf_size;
    logNum = log_num;
    sem_init(&semPro, 0, 1);
    sem_init(&semCon, 0, 0);
    threadVec.clear();
    threadVec.push_back(thread(&Frame::proThread, this));
    for(int i = 0; i < threadNum; ++i)
    {
        threadVec.push_back(thread(&Frame::conThread, this));
    }
    return true;
}


void Frame::run()
{
    for(int i = 0; i < threadVec.size(); ++i)
    {
        threadVec[i].join();
    }
}

void Frame::proThread()
{
    string line;
    int line_num = 0;
    int i = 0;
    bool finished_flag = false;
    while(true)
    {
        sem_wait(&semPro); // 等待生产者进程
        bufMtx.lock();
        for(i = 0; i < bufSize; ++i)
        {
            if(!getline(cin, line)) //数据读入
            {
                finished_flag = true;
                break;
            }
            line_num++;
            buffer.push(line);
            if(line_num%logNum == 0)
            {
                cout << line_num << " lines have finished" << endl;
            }
        }
        bufMtx.unlock();
        sem_post(&semCon); // 释放消费者进程
        if(finished_flag)
        {
            break;
        }
    }
}


void Frame::conThread(){
    bool finished_flag = false;
    vector<string> input_vec;
    input_vec.reserve(bufSize);
    while(true)
    {
        input_vec.clear();
        sem_wait(&semCon); // 等待消费者进程
        bufMtx.lock();
        for(int i = 0; i < bufSize; ++i)
        {
            if(buffer.empty())
            {
                finished_flag = true;
                break;
            }
            input_vec.push_back(buffer.front());
            buffer.pop();
        }
        bufMtx.unlock();
        sem_post(&semPro);  // 释放生产者进程
        pTask->run(input_vec); // do my thing
        if(finished_flag)
            break;
    }
    sem_post(&semCon);
}
#include "Frame.h"
#include "MyTask.h"

int main()
{
    MyTask task;
    Frame frame;
    frame.init(task, 2, 5, 5); //申请2个线程
    frame.run();
    return 0;
}

发布了126 篇原创文章 · 获赞 219 · 访问量 21万+

猜你喜欢

转载自blog.csdn.net/yz930618/article/details/88743340
今日推荐