目录
线程池
关于生活中的池化技术
我们在生活当中,把一些东西预先存储起来。好比疫情的时候我们会预先存储一些菜和生活用品,都是一种池化技术的表现。
关于线程的池化技术
我们在线程这里:我们所有的任务都需要让线程去处理,当任务来的时候,不要有任务才创建线程,而是预先把一些线程创建好,有任务,这些线程竞争的去申请任务,把任务拿走,去处理。
线程池中的线程
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 生产者和消费者模型
本质区别是:
消费者会拿走数据,读者不会
场景
一次发布,很长时间不做修改的,大部分时间都是被读取的。
在任何一个时刻,只允许一个写者写入,但是可能允许多个读者读取(写者阻塞)
优先问题
读者优先:
读者来的时候要先进行读(写者要等读者读完才能写)。写者优先:
写者来的时候要先进行写(读者要等写者写完才能读)。