当前位置: 首页 > article >正文

【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;
}


http://www.kler.cn/a/6091.html

相关文章:

  • Linux进程
  • 【动手学强化学习】安装gym subprocess-exited-with-error
  • 基于html+css的evenly布局
  • ChatGP使用案例之写文章
  • 【数据结构】第三章 栈 队列和数组
  • 华为OD机试用java实现 -【高效的任务规划】
  • LinuxFTP文件传输服务和DNS域名解析服务
  • SpringAOP入门及纯注解开发
  • ros中动态参数dynamic_reconfigure客户端
  • 一套完整的动环监控系统,适用于各类机房、学校机房、医院机房、银行库房等
  • 汽车开放系统架构
  • 隐藏个人信息
  • 为什么数字化转型,必须是“一把手”工程?
  • @Controller和@RestController的区别
  • 2023年全国最新高校辅导员精选真题及答案38
  • 【测试开发】python 应用时间做事情
  • 3.JAVA基础面试题:其他
  • git pull git push的详细使用
  • 帮公司面试了一个32岁的程序员,只因这一个细节,被我一眼看穿是培训班出来的,没啥工作经验...
  • C++笔记——第十二篇 二叉搜索树