【Linux】线程互斥
目录
C++11线程
C++线程封装
线程互斥
互斥锁
封装互斥锁
C++11线程
C++11的多线程,本质是pthread库的封装
#include <iostream>
#include <thread>
#include <unistd.h>
using namespace std;
void thread_run() {
while (1) {
cout << "我是新线程..." << endl;
sleep(1);
}
}
int main() {
thread t1(thread_run);
while (1) {
cout << "我是主线程..." << endl;
sleep(1);
}
t1.join();
return 0;
}
C++线程封装
mythread.hpp
#pragma once
#include <iostream>
#include <string>
#include <functional>
#include <pthread.h>
#include <cassert>
#include <cstdlib>
class Thread;
class Context {
public:
Thread* _this;
void* _args;
Context() : _this(nullptr), _args(nullptr) {}
~Context() {}
};
class Thread {
typedef std::function<void*(void*)> func_t;
public:
Thread(func_t func, void* args, int number)
: _func(func), _args(args) {
char buffer[128];
snprintf(buffer, sizeof(buffer), "thread-%d", number);
_name = buffer;
// start
Context* ctx = new Context();
ctx->_this = this;
ctx->_args = _args;
int n = pthread_create(&_tid, nullptr,start_routine, ctx);
assert(n == 0);
}
// 类内成员,有缺省参数
// 在类内创建线程,想让线程执行对应的方法,需要将方法设置成static
static void* start_routine(void* args) {
Context* ctx = static_cast<Context*>(args);
void* ret = ctx->_this->run(ctx->_args);
delete ctx;
return ret;
// 静态方法不能调用成员方法或者成员变量
// return _func(_args);
}
void join() {
int n = pthread_join(_tid, nullptr);
assert(n == 0);
}
void* run(void* args) {
_func(args);
}
private:
std::string _name;
func_t _func;
void* _args;
pthread_t _tid;
};
mythread.cc
#include "mythread.hpp"
#include <unistd.h>
#include <memory>
void* thread_run(void* args) {
std::string work_type = static_cast<const char*>(args);
while (true) {
std::cout << "我是一个新线程, 我正在做: " << work_type << std::endl;
sleep(1);
}
}
int main() {
std::unique_ptr<Thread> thread1(new Thread(thread_run, (void*)"hello thread1. ", 1));
std::unique_ptr<Thread> thread2(new Thread(thread_run, (void*)"hello thread2.. ", 2));
std::unique_ptr<Thread> thread3(new Thread(thread_run, (void*)"hello thread3... ", 3));
thread1->join();
thread2->join();
thread3->join();
return 0;
}
线程互斥
对变量进行++、--,在C、C++中一条语句,但在os中是至少三条指令:
1. 从内存读取数据到cpu寄存器中
2. 在寄存器对数据进行对应的算逻运算
3. 写回新的结果到内存中变量的位置
全局变量在没有保护的情况下,并不安全
多个执行流进行安全访问的共享资源 ------ 临界资源
多个执行流中访问临界资源的代码 ------ 临界区
想让多个线程串行访问共享资源 ------ 互斥
对一个资源进行访问的,要么不做,要么做完 ------- 原子性
#include "mythread.hpp"
#include <unistd.h>
#include <memory>
// 多个线程交叉执行的本质:让调度器尽可能频繁发生线程调度与切换
// 线程一般在什么时间发生切换呢?时间片到了,来了更高优先级的线程,线程等待的时候
// 线程检测一般是在什么时候呢?从内核态返回用户态的时候,线程要对调度状态进行检测,如果可以,就直接发送线程切换
// 共享资源, 火车票
int tickets = 10000;
void* thread_run(void* args) {
std::string username = static_cast<const char*>(args);
while (true) {
// 假设票数为 1 时
// 线程1将tickets=1写入寄存器中被切走
// 此时线程2也将tickets=1写入寄存器被切走,线程3、线程4类似
// 然后线程1进入if语句内,--tickets,线程2、线程3、线程4也依次 --tickets
// 故结果出现负值
if (tickets > 0) {
usleep(1000); // 单位为微妙, 1 秒 == 1000 毫秒 == 1000 000 微妙 == 1000 000 000 纳秒
std::cout << username << " 正在抢票中: " << tickets << std::endl;
--tickets;
}
else {
break;
}
}
}
int main() {
std::unique_ptr<Thread> thread1(new Thread(thread_run, (void*)"user1", 1));
std::unique_ptr<Thread> thread2(new Thread(thread_run, (void*)"user2", 2));
std::unique_ptr<Thread> thread3(new Thread(thread_run, (void*)"user3", 3));
std::unique_ptr<Thread> thread4(new Thread(thread_run, (void*)"user4", 4));
thread1->join();
thread2->join();
thread3->join();
thread4->join();
return 0;
}
互斥锁
锁本身就是一个共享资源,加锁的过程也是原子的
锁申请成功,进入临界资源,其他线程阻塞等待
锁申请成功,访问临界资源时别切走,其他线程不能申请锁,也便无法向后执行
在使用锁的时候,保证粒度尽量小
加锁是同步行为,对公共资源保护的时候要所有线程都加锁
创建互斥锁的方法多样,底层原理还是以下三个函数接口
#include "mythread.hpp"
#include <unistd.h>
#include <memory>
#include <vector>
#define NUM 4
// 多个线程交叉执行的本质:让调度器尽可能频繁发生线程调度与切换
// 线程一般在什么时间发生切换呢?时间片到了,来了更高优先级的线程,线程等待的时候
// 线程检测一般是在什么时候呢?从内核态返回用户态的时候,线程要对调度状态进行检测,如果可以,就直接发送线程切换
class ThreadData {
public:
ThreadData(const std::string& threadname, pthread_mutex_t* pmutex)
: _threadname(threadname), _pmutex(pmutex) {};
~ThreadData() {};
std::string _threadname;
pthread_mutex_t*_pmutex;
};
// 共享资源, 火车票
int tickets = 5000;
void* GetTicket(void* args) {
// std::string username = static_cast<const char*>(args);
ThreadData* td = static_cast<ThreadData*>(args);
while (true) {
// 加锁和解锁的过程是由多个线程串行执行,程序变慢
// 锁只规定互斥访问,没有规定由谁优先执行
// 锁就是真正的让多个执行流进行竞争的结果
pthread_mutex_lock(td->_pmutex);
// 上锁与解锁之间的代码就是临界区
if (tickets > 0) {
usleep(1000); // 单位为微妙, 1 秒 == 1000 毫秒 == 1000 000 微妙 == 1000 000 000 纳秒
std::cout << td->_threadname << " 正在抢票中: " << tickets << std::endl;
--tickets;
pthread_mutex_unlock(td->_pmutex);
}
else {
pthread_mutex_unlock(td->_pmutex);
break;
}
usleep(100); //模拟形成一个订单给用户
}
}
int main() {
// 初始化锁
pthread_mutex_t lock;
pthread_mutex_init(&lock, nullptr);
std::vector<pthread_t> tids(NUM);
for (int i = 0; i < NUM; ++i) {
char buffer[64];
snprintf(buffer, sizeof(buffer), "thread %d", i + 1);
ThreadData* td = new ThreadData(buffer, &lock);
pthread_create(&tids[i], nullptr, GetTicket, td);
}
for (const auto& tid: tids) {
pthread_join(tid, nullptr);
}
// 释放锁
pthread_mutex_destroy(&lock);
return 0;
}
封装互斥锁
当对象被构建的时候自动加锁,当对象被析构的时候解锁
mymutex.hpp
#pragma once
#include <iostream>
#include <pthread.h>
class Mutex {
public:
Mutex(pthread_mutex_t* plock = nullptr)
: _plock(plock) {};
void lock() {
if (_plock) pthread_mutex_lock(_plock);
}
void unlock() {
if (_plock) pthread_mutex_unlock(_plock);
}
private:
pthread_mutex_t* _plock;
};
class LockGuard {
public:
LockGuard(pthread_mutex_t* mutex)
: _mutex(mutex) {
_mutex.lock(); //在构造函数中进行加锁
};
~LockGuard() {
_mutex.unlock(); //在析构函数中解锁
}
private:
Mutex _mutex;
};
mythread.cc
#include "mythread.hpp"
#include "mymutex.hpp"
#include <unistd.h>
#include <memory>
#include <vector>
#define NUM 4
// 多个线程交叉执行的本质:让调度器尽可能频繁发生线程调度与切换
// 线程一般在什么时间发生切换呢?时间片到了,来了更高优先级的线程,线程等待的时候
// 线程检测一般是在什么时候呢?从内核态返回用户态的时候,线程要对调度状态进行检测,如果可以,就直接发送线程切换
class ThreadData {
public:
ThreadData(const std::string& threadname, pthread_mutex_t* pmutex)
: _threadname(threadname), _pmutex(pmutex) {};
~ThreadData() {};
std::string _threadname;
pthread_mutex_t*_pmutex;
};
// 共享资源, 火车票
int tickets = 2000;
void* GetTicket(void* args) {
// std::string username = static_cast<const char*>(args);
ThreadData* td = static_cast<ThreadData*>(args);
while (true) {
// 设置代码块
{
// 加锁和解锁的过程是由多个线程串行执行,程序变慢
// 锁只规定互斥访问,没有规定由谁优先执行
// 锁就是真正的让多个执行流进行竞争的结果
// pthread_mutex_lock(td->_pmutex);
LockGuard lock_guard(td->_pmutex);
// 上锁与解锁之间的代码就是临界区
if (tickets > 0) {
usleep(1000); // 单位为微妙, 1 秒 == 1000 毫秒 == 1000 000 微妙 == 1000 000 000 纳秒
std::cout << td->_threadname << " 正在抢票中: " << tickets << std::endl;
--tickets;
// pthread_mutex_unlock(td->_pmutex);
}
else {
// pthread_mutex_unlock(td->_pmutex);
break;
}
}
usleep(100); //模拟形成一个订单给用户
}
}
int main() {
// 初始化锁
pthread_mutex_t lock;
pthread_mutex_init(&lock, nullptr);
std::vector<pthread_t> tids(NUM);
for (int i = 0; i < NUM; ++i) {
char buffer[64];
snprintf(buffer, sizeof(buffer), "thread %d", i + 1);
ThreadData* td = new ThreadData(buffer, &lock);
pthread_create(&tids[i], nullptr, GetTicket, td);
}
for (const auto& tid: tids) {
pthread_join(tid, nullptr);
}
// 释放锁
pthread_mutex_destroy(&lock);
return 0;
}