Linux——线程池|单例模式|常见的锁|读写者问题

目录

线程池

关于生活中的池化技术

关于线程的池化技术

线程池中的线程

代码

单例模式

代码

常见的锁

读写者问题

关系

与生产者消费者模型作比较

场景

优先问题


线程池

关于生活中的池化技术

我们在生活当中,把一些东西预先存储起来。好比疫情的时候我们会预先存储一些菜和生活用品,都是一种池化技术的表现。

关于线程的池化技术

我们在线程这里:我们所有的任务都需要让线程去处理,当任务来的时候,不要有任务才创建线程,而是预先把一些线程创建好,有任务,这些线程竞争的去申请任务,把任务拿走,去处理。

线程池中的线程

1、有任务就处理
2、没有任务就休眠

代码

线程池代码

________________________________________________________________________________________________________
ThreadPool.hpp
____________________________________________________
__________________________________________________

#include "ThreadPool.hpp"
#include "Thread.hpp"
#include "Task.hpp"
#include <memory>
#include <unistd.h>

int main()
{
    std::unique_ptr<ThreadPool<Task>> tp(new ThreadPool<Task>());
    tp->run();

    int x, y;
    char op;
    while (true)
    {
        std::cout << "请输入数据1# ";
        std::cin >> x;
        std::cout << "请输入数据2# ";
        std::cin >> y;
        std::cout << "请输入你要进行的运算# ";
        std::cin >> op;
        Task t(x, y, op, mymath);
        // std::cout << "你刚刚录入了一个任务:" << t.toTaskString() << ",确认提交吗?[y/n]# ";

        // char confirm;
        // std::cin >> confirm;
        // if (confirm == 'y')
        //     tp->push(t);
        tp->push(t);

        sleep(1);
    }
}



**************************************************************************************************
Task.hpp
**************************************************************************************************

#pragma once

#include <iostream>
#include <functional>
#include <string>
#include <cstdio>

const std::string oper = "+-*/%";

int mymath(int x, int y, char op)
{
    int result = 0;
    switch (op)
    {
    case '+':
        result = x + y;
        break;
    case '-':
        result = x - y;
        break;
    case '*':
        result = x * y;
        break;

    case '/':
    {
        if (y == 0)
        {
            std::cerr << "mod zero error!" << std::endl;
            result = -1;
        }
        else
            result = x / y;
    }
    break;
    case '%':
    {
        if (y == 0)
        {
            std::cerr << "mod zero error!" << std::endl;
            result = -1;
        }
        else
            result = x % y;
    }
    break;
    default:
        break;
    }

    return result;
}

// 计算数据的类
class Task
{

    using func_t = std::function<int(int, int, char)>;
    // typedef std::function<int(int, int)> func_t;

public:
    Task()
    {
    }

    Task(int x, int y, char op, func_t func)
        : _x(x), _y(y), _op(op), _callback(func)
    {
    }

    // 该函数是消费任务的打印信息
    std::string operator()()
    {
        int result = _callback(_x, _y, _op);
        char buffer[64];
        snprintf(buffer, sizeof(buffer), "%d %c %d = %d", _x, _op, _y, result);
        return buffer;
    }

    // 该函数是生产任务的打印信息

    std::string toTaskString()
    {
        char buffer[64];
        snprintf(buffer, sizeof(buffer), "%d %c %d = ?", _x, _op, _y);
        return buffer;
    }

private:
    int _x;
    int _y;
    char _op;
    func_t _callback;
};

**************************************************************************************************
LockGuard.hpp
**************************************************************************************************
#pragma once

#include <iostream>
#include <pthread.h>

// 写了一个锁的类,里面有加锁和解锁的成员函数
// 并且有一个锁的成员变量
class Mutex
{
public:
    Mutex(pthread_mutex_t *lock_p = nullptr) : lock_p_(lock_p)
    {
    }
    void lock()
    {
        if (lock_p_)
            pthread_mutex_lock(lock_p_);
    }

    void unlock()
    {
        if (lock_p_)
            pthread_mutex_unlock(lock_p_);
    }

    ~Mutex()
    {
    }

private:
    pthread_mutex_t *lock_p_; 
};

// 这个类中定义了一个上述类中的对象
// 并且将上述类中的成员函数放进该类中的构造函数和析构函数中
// guard:警卫
class LockGuard
{
public:
    LockGuard(pthread_mutex_t *mutex):mutex_(mutex)
    {
        mutex_.lock(); // 在构造函数中进行加锁
    }
    ~LockGuard()
    {
        mutex_.unlock();// 在析构函数中进行解锁
    }
private:
    Mutex mutex_;

};


**************************************************************************************************
Thread.hpp
**************************************************************************************************
#pragma once

#include <iostream>
#include <pthread.h>
#include <functional>
#include <cassert>
#include <cstring>
#include <string>

namespace ThreadNs
{
    typedef std::function<void *(void *)> func_t;
    const int num = 1024;

    class Thread
    {
    private:
        // 在类内创建线程,想让线程执行对应的方法,需要将方法设置成为static
        // 类内成员,有缺省参数!在第一参数中包含了一个this指针
        static void *start_routine(void *args)
        {
            Thread *_this = static_cast<Thread *>(args);

            return _this->callback();
        }

    public:
        Thread()
        {
            char namebuffer[num];
            snprintf(namebuffer, sizeof namebuffer, "thread-%d", threadnum++);
            name_ = namebuffer;
        }

        void start(func_t func, void *args = nullptr)
        {
            func_ = func;
            args_ = args;
            // 第四个参数传入的是线程对象
            int n = pthread_create(&tid_, nullptr, start_routine, this);
            assert(n == 0);
            (void)n;
        }

        void join()
        {
            int n = pthread_join(tid_, nullptr);
            assert(n == 0);

            (void)n;
        }

        void *callback() { return func_(args_); }

        ~Thread()
        {
            // do northing
        }

        std::string threadname()
        {
            return name_;
        }

    private:
        std::string name_;
        func_t func_;
        void *args_;
        pthread_t tid_;

        // 注意静态成员变量不能在类内初始化
        static int threadnum;
    };

    int Thread::threadnum = 1;

} // namespace ThreadNs done


**************************************************************************************************main.cc
**************************************************************************************************
#include "ThreadPool.hpp"
#include "Thread.hpp"
#include "Task.hpp"
#include <memory>
#include <unistd.h>

int main()
{
    std::unique_ptr<ThreadPool<Task>> tp(new ThreadPool<Task>());
    tp->run();

    int x, y;
    char op;
    while (true)
    {
        std::cout << "请输入数据1# ";
        std::cin >> x;
        std::cout << "请输入数据2# ";
        std::cin >> y;
        std::cout << "请输入你要进行的运算# ";
        std::cin >> op;
        Task t(x, y, op, mymath);
        // std::cout << "你刚刚录入了一个任务:" << t.toTaskString() << ",确认提交吗?[y/n]# ";

        // char confirm;
        // std::cin >> confirm;
        // if (confirm == 'y')
        //     tp->push(t);
        tp->push(t);

        sleep(1);
    }
}

























单例模式

代码

// ThreadPool.hpp


#pragma once

#include "Thread.hpp"
#include "LockGuard.hpp"
#include <vector>
#include <queue>
#include <pthread.h>
#include <unistd.h>
#include <mutex>

using namespace ThreadNs;

const int gnum = 3;

template <class T>
class ThreadPool;

template <class T>
class ThreadData
{
public:
    ThreadPool<T> *threadpool;
    std::string name;

public:
    ThreadData(ThreadPool<T> *tp, const std::string &n)
        : threadpool(tp), name(n)
    {
    }
};

template <class T>
class ThreadPool
{

private:
    static void *handlerTask(void *args)
    {
        ThreadData<T> *td = (ThreadData<T> *)args;
        while (true)
        {
            T t;
            {
                LockGuard lockguard(td->threadpool->mutex());

                while (td->threadpool->isQueueEmpty())
                {
                    td->threadpool->threadWait();
                }

                // pop的本质,是将任务从公共的队列当中,拿到当前线程自己独立的栈中
                t = td->threadpool->pop();
            }
            // 处理任务放在加锁与解锁之外
            std::cout << td->name
                      << "获取了一个任务: " << t.toTaskString() << " 并处理完成,结果是:" << t() << std::endl;
        }
        delete td;
        return nullptr;
    }
    // 单例模式:构造函数设置为私有
    ThreadPool(const int &num = gnum) : _num(num)
    {
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_cond, nullptr);

        for (int i = 0; i < _num; i++)
        {
            _threads.push_back(new Thread());
        }
    }
    void operator=(const ThreadPool &) = delete;
    ThreadPool(const ThreadPool &) = delete;

    // 为了解决static handlerTask
public:
    void lockQueue()
    {
        pthread_mutex_lock(&_mutex);
    }

    void unlockQueue()
    {
        pthread_mutex_unlock(&_mutex);
    }
    bool isQueueEmpty()
    {
        return _task_queue.empty();
    }
    void threadWait()
    {
        pthread_cond_wait(&_cond, &_mutex);
    }
    T pop()
    {
        T t = _task_queue.front();
        _task_queue.pop();
        return t;
    }
    pthread_mutex_t *mutex()
    {
        return &_mutex;
    }

public:
    void run()
    {
        for (const auto &t : _threads)
        {
            ThreadData<T> *td = new ThreadData<T>(this, t->threadname());

            t->start(handlerTask, td);
            std::cout << t->threadname() << " start ..." << std::endl;
        }
    }

    void push(const T &in)
    {
        LockGuard lockguard(&_mutex);
        _task_queue.push(in);
        pthread_cond_signal(&_cond);
    }

    ~ThreadPool()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_cond);

        for (const auto &t : _threads)
            delete t;
    }

    static ThreadPool<T> *getInstance()
    {
        // 最外边这个if的作用是:
        // 如果tp已经被创建,那么就不用创建了,因为是单例模型
        if (nullptr == tp)
        {
            _singlock.lock();
            if (nullptr == tp)
            {
                tp = new ThreadPool<T>();
            }
            _singlock.unlock();
        }

        return tp;
    }

private:
    int _num;

    std::vector<Thread *> _threads; // 线程集合
    std::queue<T> _task_queue;      // 任务队列

    pthread_mutex_t _mutex;
    pthread_cond_t _cond;

    static ThreadPool<T> *tp;
    static std::mutex _singlock;
};

template <class T>
ThreadPool<T> *ThreadPool<T>::tp = nullptr;

template <class T>
std::mutex ThreadPool<T>::_singlock;



// Task.hpp

#pragma once

#include <iostream>
#include <functional>
#include <string>
#include <cstdio>

const std::string oper = "+-*/%";

int mymath(int x, int y, char op)
{
    int result = 0;
    switch (op)
    {
    case '+':
        result = x + y;
        break;
    case '-':
        result = x - y;
        break;
    case '*':
        result = x * y;
        break;

    case '/':
    {
        if (y == 0)
        {
            std::cerr << "mod zero error!" << std::endl;
            result = -1;
        }
        else
            result = x / y;
    }
    break;
    case '%':
    {
        if (y == 0)
        {
            std::cerr << "mod zero error!" << std::endl;
            result = -1;
        }
        else
            result = x % y;
    }
    break;
    default:
        break;
    }

    return result;
}

// 计算数据的类
class Task
{

    using func_t = std::function<int(int, int, char)>;
    // typedef std::function<int(int, int)> func_t;

public:
    Task()
    {
    }

    Task(int x, int y, char op, func_t func)
        : _x(x), _y(y), _op(op), _callback(func)
    {
    }

    // 该函数是消费任务的打印信息
    std::string operator()()
    {
        int result = _callback(_x, _y, _op);
        char buffer[64];
        snprintf(buffer, sizeof(buffer), "%d %c %d = %d", _x, _op, _y, result);
        return buffer;
    }

    // 该函数是生产任务的打印信息

    std::string toTaskString()
    {
        char buffer[64];
        snprintf(buffer, sizeof(buffer), "%d %c %d = ?", _x, _op, _y);
        return buffer;
    }

private:
    int _x;
    int _y;
    char _op;
    func_t _callback;
};


// LockGuard.hpp

#pragma once

#include <iostream>
#include <pthread.h>

// 写了一个锁的类,里面有加锁和解锁的成员函数
// 并且有一个锁的成员变量
class Mutex
{
public:
    Mutex(pthread_mutex_t *lock_p = nullptr) : lock_p_(lock_p)
    {
    }
    void lock()
    {
        if (lock_p_)
            pthread_mutex_lock(lock_p_);
    }

    void unlock()
    {
        if (lock_p_)
            pthread_mutex_unlock(lock_p_);
    }

    ~Mutex()
    {
    }

private:
    pthread_mutex_t *lock_p_; 
};

// 这个类中定义了一个上述类中的对象
// 并且将上述类中的成员函数放进该类中的构造函数和析构函数中
// guard:警卫
class LockGuard
{
public:
    LockGuard(pthread_mutex_t *mutex):mutex_(mutex)
    {
        mutex_.lock(); // 在构造函数中进行加锁
    }
    ~LockGuard()
    {
        mutex_.unlock();// 在析构函数中进行解锁
    }
private:
    Mutex mutex_;

};

// Thread.hpp

#pragma once

#include <iostream>
#include <pthread.h>

// 写了一个锁的类,里面有加锁和解锁的成员函数
// 并且有一个锁的成员变量
class Mutex
{
public:
    Mutex(pthread_mutex_t *lock_p = nullptr) : lock_p_(lock_p)
    {
    }
    void lock()
    {
        if (lock_p_)
            pthread_mutex_lock(lock_p_);
    }

    void unlock()
    {
        if (lock_p_)
            pthread_mutex_unlock(lock_p_);
    }

    ~Mutex()
    {
    }

private:
    pthread_mutex_t *lock_p_; 
};

// 这个类中定义了一个上述类中的对象
// 并且将上述类中的成员函数放进该类中的构造函数和析构函数中
// guard:警卫
class LockGuard
{
public:
    LockGuard(pthread_mutex_t *mutex):mutex_(mutex)
    {
        mutex_.lock(); // 在构造函数中进行加锁
    }
    ~LockGuard()
    {
        mutex_.unlock();// 在析构函数中进行解锁
    }
private:
    Mutex mutex_;

};


// main.cc


#include "ThreadPool.hpp"
#include "Thread.hpp"
#include "Task.hpp"
#include <memory>
#include <unistd.h>

int main()
{
    // std::unique_ptr<ThreadPool<Task>> tp(new ThreadPool<Task>());
    ThreadPool<Task>::getInstance()->run();
    // tp->run();

    int x, y;
    char op;
    while (true)
    {
        std::cout << "请输入数据1# ";
        std::cin >> x;
        std::cout << "请输入数据2# ";
        std::cin >> y;
        std::cout << "请输入你要进行的运算# ";
        std::cin >> op;
        Task t(x, y, op, mymath);
        // std::cout << "你刚刚录入了一个任务:" << t.toTaskString() << ",确认提交吗?[y/n]# ";

        // char confirm;
        // std::cin >> confirm;
        // if (confirm == 'y')
        //     tp->push(t);
        ThreadPool<Task>::getInstance()->push(t);

        sleep(1);
    }
}







常见的锁

悲观锁:在每次取数据时,总是担心数据会被其他线程修改,所以会在取数据前先加锁(读锁,写锁,行锁等),当其他线程想要访问数据时,被阻塞挂起。
乐观锁:每次取数据时候,总是乐观的认为数据不会被其他线程修改,因此不上锁。但是在更新数据前,会判断其他数据在更新前有没有对数据进行修改。主要采用两种方式:版本号机制和CAS操作。
CAS操作:当需要更新数据时,判断当前内存值和之前取得的值是否相等。如果相等则用新值更新。若不等则失败,失败则重试,一般是一个自旋的过程,即不断重试。

读写者问题

关系

 写者与写者 互斥关系

 读者与写者 互斥和同步关系

 读者与读者 没有任何关系

与生产者消费者模型作比较

读者写者模型 vs 生产者和消费者模型 

本质区别是:

消费者会拿走数据,读者不会

场景

一次发布,很长时间不做修改的,大部分时间都是被读取的。

在任何一个时刻,只允许一个写者写入,但是可能允许多个读者读取(写者阻塞)

优先问题

读者优先:
读者来的时候要先进行读(写者要等读者读完才能写)。

写者优先:
写者来的时候要先进行写(读者要等写者写完才能读)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

袁百万

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值