Linux——线程的同步与互斥

这篇具有很好参考价值的文章主要介绍了Linux——线程的同步与互斥。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

模拟抢火车票的过程

代码示例

thread.cc

Thread.hpp

运行结果

分析原因

tickets减到-2的本质 

解决抢票出错的方案

临界资源的概念

原子性的概念

加锁

定义

初始化

销毁

代码形式如下

代码示例1:

代码示例2:

总结

如何看待锁

申请失败将会阻塞

 pthread_mutex_tyrlock

互斥锁实现原理

 锁是如何实现互斥的?

封装加锁组件(RAII风格)

Mutex.hpp

mythread.cc

RAII风格

可重入与线程的关系(浅谈)

可重入与线程安全联系

可重入与线程安全区别

死锁

概念

提出问题

死锁的四个必要条件

如何破坏死锁

概念:

线程同步

本质

先谈生产者消费者模型

"321"原则

生产者消费者模型的特点

条件变量

概念

定义

具体操作 

所用函数

定义的返回值

代码示例:

 唤醒方式


模拟抢火车票的过程

代码示例

thread.cc


#include <iostream>
#include <pthread.h>
#include <cstdio>
#include <unistd.h>
#include <memory>
#include <cstring>
#include "Thread.hpp"

// pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;

// 共享资源, 火车票
int tickets = 10000;
// 就需要尽可能的让多个线程交叉执行
// 多个线程交叉执行本质:就是让调度器尽可能的频繁发生线程调度与切换
// 线程一般在什么时候发生切换呢?时间片到了,来了更高优先级的线程,线程等待的时候。
// 线程是什么时候检测上面的问题呢?从内核态返回用户态的时候,线程要对调度状态进行检测
// 如果可以,就发生切换。

// 1、多个执行流进行安全访问的共享资源 - 临界资源?
// 2、我们把多个执行流中,访问临界资源的代码 -- 临界区 -- 往往是线程代码很小的一部分
// 3、想让多个线程串行访问共享资源 -- 互斥
// 4、对一个资源进行访问的时候,要么不做,要么做完 -- 原子性
// 一个对资源进行操作,如果只用一条汇编语句就能完成 -- 原子性
// 反之:不是原子的 -- 当前理解,方便表述

// 解决方案:加锁

void *getTicket(void *args)
{
    // 获取线程的名字
    std::string username = static_cast<const char *>(args);

    while (true)
    {
        // pthread_mutex_lock(&lock);
        if (tickets > 0)
        {
            usleep(1000);

            // 只有票数大于0才值得抢
            std::cout << username << "正在抢票" << tickets-- << std::endl;
            // 用这段时间来模拟真实的抢票要花费的时间
            // pthread_mutex_unlock(&lock);
        }
        else
        {
            // pthread_mutex_unlock(&lock);

            break;
        }
    }
    return nullptr;
}

int main()
{

    std::unique_ptr<Thread> thread1(new Thread(getTicket, (void *)"user1", 1));
    std::unique_ptr<Thread> thread2(new Thread(getTicket, (void *)"user2", 2));
    std::unique_ptr<Thread> thread3(new Thread(getTicket, (void *)"user3", 3));
    std::unique_ptr<Thread> thread4(new Thread(getTicket, (void *)"user4", 4));

    thread1->join();
    thread2->join();
    thread3->join();
    thread4->join();

    return 0;
}

Thread.hpp


#pragma once

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

class Thread;

// 上下文
class Context
{
public:
    Thread *this_;
    void *args_;
public:
    Context():this_(nullptr),args_(nullptr)
    {}
    ~Context()
    {}
};





class Thread
{
public:
    typedef std::function<void *(void *)> func_t;
    const int num = 1024;

public:
    Thread(func_t func, void *args, int number)
        : func_(func), args_(args)
    {

        // name_="thread-";
        // name_+=std::to_string(number);

        char buffer[num];
        snprintf(buffer, sizeof buffer, "thread-%d", number);
        name_ = buffer;
        
        // 异常 == if : 意料之外用异常或者if判断
        // assert:意料之中用assert

        Context *ctx=new Context();

        ctx->this_=this;
        ctx->args_=args_;



        int n = pthread_create(&tid_, nullptr, start_routine,ctx); // TODO
        assert(n == 0);
        (void)n;
        // 编译debug的方式发布的时候是存在的,release方式发布,
        // assert就不存在l,n就是一个定义,
        // 但是没有使用的变量,有些编译器下会有warning
    }

    // 在类内创建线程,想让线程执行对应的方法,需要将方法设置成为static
    // 类内成员,有缺省参数!在第一参数中包含了一个this指针
    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)n;
    }


    void *run(void *args)
    {
        return func_(args);
    }


    ~Thread()
    {
        // do northing
    }

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

    pthread_t tid_;
};

运行结果

user2正在抢票16
user4正在抢票15
user1正在抢票14
user3正在抢票13
user2正在抢票12
user4正在抢票11
user1正在抢票10
user3正在抢票9
user2正在抢票8
user4正在抢票7
user1正在抢票6
user3正在抢票5
user2正在抢票4
user4正在抢票3
user1正在抢票2
user3正在抢票1
user2正在抢票0
user4正在抢票-1
user1正在抢票-2

分析原因

很明显,我们只有10000张票,可是运行结果中抢到了-2。比如这个那么这多出的3张票对应的人将没地方坐。那么为什么会出现这种问题呢,下面将来解答。

抢票出错的本质:

多个线程交叉执行!

多个线程交叉执行本质:

调度器频繁发生线程调度与切换。

线程一般在什么时候发生切换呢?

1、时间片到了

2、来了更高优先级的线程,线程等待的时候。

线程是什么时候检测上面的问题呢?

从内核态返回用户态的时候,线程要对调度状态进行检测,如果可以,就发生切换。

判断票是否大于0的本质逻辑是:

if(tickets>0)

1、读取内存数据CPU内的寄存器中
2、进行判断

tickets--的本质逻辑是:

1、读取数据
2、更改数据
3、写回数据

对变量进行++,或者--,在C、C++上,看起来只有一条语句,但是汇编之后至少是三条语句:

1、从内存中读取数据到CPU内的寄存器中
2、在寄存器中让CPU进行对应的算逻运算
3、写回新的结果到内存中变量的位置

tickets减到-2的本质 

 Linux——线程的同步与互斥

 文章来源地址https://www.toymoban.com/news/detail-460154.html

全局变量
int g_val=1000;

寄存器只有一套,因此不同的线程在寄存器的存放的上下文为各个线程私有

线程A在做完 1 和 2 后 带着自己的上下文切走了
1000->999


线程B在做完1 和 2 后也带着自己的上下文切走了        

结果现在线程A切换回来了(切回来之后需要让自己的上下文给重新写入寄存器) 又把数据改为999了。

我们定义的全局变量,在没有保护的时候,往往是不安全的,像上面多个线程在交替执行造成的数据安全问题,发生了数据不一致问题!

解决抢票出错的方案

我们可以使用加锁的方式来避免这种情况发生

临界资源的概念

1、多个执行流进行安全访问的共享资源 - 临界资源
2、我们把多个执行流中,访问临界资源的代码 -- 临界区 -- 往往是线程代码的很小的一部分
3、想让多个线程串行访问资源 -- 互斥
4、对一个资源进行访问的时候,要么不做,要么做完 -- 原子性 

注意:并不是上述所有代码都属于临界区,而是访问临界资源的代码是临界区 。临界区的代码往往粒度(代码长度)很小。

原子性的概念

原子性:一个资源进行的操作,如果只用一条汇编就能完成 -- 就是原子的      反之不是。

加锁

定义

可以把这个锁定义为全局的/静态的 -- 不用初始化和销毁
直接定义为 :
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

初始化

int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);

销毁

int pthread_mutex_destroy(pthread_mutex_t *mutex);

代码形式如下

加锁
临界区
解锁

加锁和解锁之间的区域是临界区(也就是需要加锁的地方)。

代码示例1:

只展示了getTicket函数部分(包括加锁和解锁)


void *getTicket(void *args)
{
    // 获取线程的名字
    std::string username = static_cast<const char *>(args);

    while (true)
    {
	  // 加锁
        pthread_mutex_lock(&lock);

        // 判断的本质:
        // 1、读取内存数据CPU内的寄存器中
        // 2、进行判断
        if (tickets > 0)
        {
            usleep(1000);

            // 只有票数大于0才值得抢
            std::cout << username << "正在抢票" << tickets << std::endl;

            tickets--;
            // --的本质
            // 1、读取数据
            // 2、更改数据
            // 3、写回数据


            // 用这段时间来模拟真实的抢票要花费的时间
		// 解锁
            pthread_mutex_unlock(&lock);
        }
        else
        {
		// 解锁
            pthread_mutex_unlock(&lock);

            break;
        }
    }
    return nullptr;
}

代码示例2:

使用结构体封装了线程名和锁,并且使用了原生的线程创建方式


#include <iostream>
#include <vector>
#include <pthread.h>
#include <string>
#include <cstdio>
#include <unistd.h>
#include <memory>
#include <cstring>
#include "Thread.hpp"

// 共享资源, 火车票
int tickets = 10000;
// 就需要尽可能的让多个线程交叉执行
// 多个线程交叉执行本质:就是让调度器尽可能的频繁发生线程调度与切换
// 线程一般在什么时候发生切换呢?时间片到了,来了更高优先级的线程,线程等待的时候。
// 线程是什么时候检测上面的问题呢?从内核态返回用户态的时候,线程要对调度状态进行检测
// 如果可以,就发生切换。

// 1、多个执行流进行安全访问的共享资源 - 临界资源?
// 2、我们把多个执行流中,访问临界资源的代码 -- 临界区 -- 往往是线程代码很小的一部分
// 3、想让多个线程串行访问共享资源 -- 互斥
// 4、对一个资源进行访问的时候,要么不做,要么做完 -- 原子性
// 一个对资源进行操作,如果只用一条汇编语句就能完成 -- 原子性
// 反之:不是原子的 -- 当前理解,方便表述

// 解决方案:加锁


// 使用结构体 对锁和名字进行了封装 使用的时候直接使用结构体对象调用即可
class ThreadData
{
public:
    ThreadData(const std::string &threadname, pthread_mutex_t *mutex_p)
        : threadname_(threadname), mutex_p_(mutex_p)
    {}
    ~ThreadData() {}

public:
    std::string threadname_;
    pthread_mutex_t *mutex_p_;
};

void *getTicket(void *args)
{
    // 获取线程的名字
    // std::string username = static_cast<const char *>(args);

    ThreadData *td = static_cast<ThreadData *>(args);

    while (true)
    {
        //  加锁
        pthread_mutex_lock(td->mutex_p_);

       

        // 判断的本质:
        // 1、读取内存数据CPU内的寄存器中
        // 2、进行判断
        if (tickets > 0)
        {
            usleep(1000);

            // 只有票数大于0才值得抢
            std::cout << td->threadname_ << "正在抢票" << tickets << std::endl;

            tickets--;
            // --的本质
            // 1、读取数据
            // 2、更改数据
            // 3、写回数据

            // 用这段时间来模拟真实的抢票要花费的时间

            // 解锁            				      pthread_mutex_unlock(td->mutex_p_);
        }
        else
        {
            pthread_mutex_unlock(td->mutex_p_);

            // 解锁
            break;
        }
 	  // 抢完票就完了吗? 当然不是
        usleep(1000);// 模拟形成一个订单给用户
    }
    return nullptr;
}

int main()
{
#define NUM 4

    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);

    
}

总结

加锁和解锁的过程:

多个线程由并行执行变为了串行执行的,程序变慢了!

锁只规定互斥访问,没有规定必须谁优先执行!

锁就是真正的让多个执行流进行竞争的结果。

执行结果一直是一个线程在抢票原因是:

该线程竞争能力强,其他线程则比较弱,因此每次都是该线程抢到了。

如何看待锁

  1.  锁,本身就是一个共享资源!全局的变量是要被保护的,锁是用来保护全局的资源的,锁本身也是全局资源,锁的安全谁来保护呢?
  2. pthread_mutex_lock、pthread_mutex_unlock:加锁的过程必须是安全的!加锁的过程其实是原子的!要不申请成功,要不申请失败。
  3. 如果申请成功,就继续向后执行,如果申请暂时没有成功,执行流会阻塞 

申请失败将会阻塞

Linux——线程的同步与互斥

 pthread_mutex_tyrlock

当然我们也可以使用 pthread_mutex_tyrlock(pthread_mutex_t *mutex)
函数目的是:试着去加锁
如果我加锁成功了,我会持有锁
如果加锁失败了,会出错返回

如何理解加锁和解锁的本质?

加锁的过程是原子的。

若当前线程1 线程2 线程3 同时访问临界资源的时候,如果线程1,申请锁成功,进入临界资源,正在访问临界资源期间,其他线程在做什么??

阻塞,等待。


如果线程1,申请锁成功,进入临界资源,正在访问临界资源期间,我可不可以被切换呢??

可以

当持有锁的线程被切走的的时候其他线程可以申请锁吗? 

当持有锁的线程被切走的时候,是抱着锁被切走的,即便自己被切走了,其他线程依旧无法申请锁成功,也便无法向后执行!直到我最终释放这个锁!

 

所以,对于其他线程而言,有意义的锁的状态,无非两种

1.申请锁前
2.申请锁后
站在其他线程角度,看待当前线程持有锁的过程就是原子的!!

使用锁注意事项:

1、未来我们在使用锁的时候,一定要尽量保证临界区的粒度(代码长度)非常小。

2、加锁是程序员行为,必须要做到要加都要加!

互斥锁实现原理

i++或者++i都不是原子的。
为了实现互斥锁操作,大多数体系结构都提供了swap或exchange指令,该指令的作用是把寄存器和内存单元的数据相交换,由于只有一条指令,保证了原子性。

 锁是如何实现互斥的?

%al:寄存器

交换的本质:共享的数据,交换到我的上下文中!!!

lock:
    movb $0, %al  //将0放到该线程的寄存器中
    xchgb %al, mutex // 寄存器和内存单元的数据相交换
        if(al寄存器的内容 > 0)
        {
            return 0;
        }
        else
            挂起等待;
        goto lock;
unlock:
    movb $1, mutex // 将1移动到内存里面
    唤醒等待Mutex的线程;
    return 0;    


1、CPU内寄存器只有一套被所有执行流共享
2、CPU内寄存器的内容,是每个执行流私有的,运行时上下文

封装加锁组件(RAII风格)

Mutex.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_;

};

mythread.cc



#include <iostream>
#include <vector>
#include <pthread.h>
#include <string>
#include <cstdio>
#include <unistd.h>
#include <memory>
#include <cstring>
#include"Mutex.hpp"
// #include "Thread.hpp"

// // 定义全局变量的
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;

// 共享资源, 火车票
int tickets = 10000;


class ThreadData
{
public:
    ThreadData(const std::string &threadname, pthread_mutex_t *mutex_p)
        : threadname_(threadname), mutex_p_(mutex_p)
    {}
    ~ThreadData() {}

public:
    std::string threadname_;
    pthread_mutex_t *mutex_p_;
};

void *getTicket(void *args)
{
    // 获取线程的名字
    std::string username = static_cast<const char *>(args);

   

    while (true)
    {
     LockGuard lockguard(&lock);
        // 将lock传过去之后调用析构函数自动加锁
        // 然后出作用域调用析构函数自动解锁


        if (tickets > 0)
        {
            usleep(1000);


            std::cout << username << "正在抢票" << tickets << std::endl;

            tickets--;

        }
        else
        {
            // pthread_mutex_unlock(&lock);

            // 解锁
            // pthread_mutex_unlock(&lock);

            break;
        }

        // 抢完票就完了吗? 当然不是
        usleep(1000);// 模拟形成一个订单给用户
    }
    return nullptr;
}

int main()
{



    pthread_mutex_init(&lock, nullptr);
    pthread_t t1, t2, t3, t4;


    pthread_create(&t1,nullptr,getTicket,(void*)"thread 1");
    pthread_create(&t2,nullptr,getTicket,(void*)"thread 2");
    pthread_create(&t3,nullptr,getTicket,(void*)"thread 3");
    pthread_create(&t4,nullptr,getTicket,(void*)"thread 4");

    pthread_join(t1,nullptr);
    pthread_join(t2,nullptr);
    pthread_join(t3,nullptr);
    pthread_join(t4,nullptr);

    pthread_mutex_destroy(&lock);

    
}

RAII风格

重点:

 LockGuard lockguard(&lock);
 // 将lock传过去之后调用析构函数自动加锁
 // 然后出作用域调用析构函数自动解锁

可重入与线程的关系(浅谈)

可重入与线程安全联系

函数是可重入的,那就是线程安全的
函数是不可重入的,那就不能由多个线程使用,有可能引发线程安全问题
如果一个函数中有全局变量,那么这个函数既不是线程安全也不是可重入的。

可重入与线程安全区别

可重入函数是线程安全函数的一种
线程安全不一定是可重入的,而可重入函数则一定是线程安全的。
如果将对临界资源的访问加上锁,则这个函数是线程安全的,但如果这个重入函数若锁还未释放则会产生
死锁,因此是不可重入的。

死锁

概念

一组执行流(无论是进程还是线程),它在持有自己的锁资源的同时,还想方设法的去申请对方的锁资源,因为大家互相持有自己的,还互相申请对方的,因为锁是不可抢占式的锁(我拿了锁,除非我自己主动归还,否则别人是不能直接要我的锁的)。

所以,就是大家互相持有自己的资源,还在等待对方的锁资源而导致代码无法推进的情况,这种情况就叫死锁。

提出问题

1、一把锁,有可能死锁吗?

可能的,比如自己连续申请两次锁
pthread_mutex_lock(&lock);
pthread_mutex_lock(&lock);


2、为什么会有死锁? 逻辑链条

首先一定是用了锁才会发生死锁

3、为什么要用锁呢?

保证临界资源的安全
多线程访问我们可能出现数据不一致的问题
多线程&&全局资源
多线程大部分资源(全局的)是共享的
多线程的特征

引出的大概念

任何技术都有自己的边界,是解决问题的,但有可能在解决问题的同时,一定会可能引入新的问题!

死锁的四个必要条件

1.互斥   --   我们必须得保证访问一份资源是互斥的(这是我们锁基本特性,没有互斥那就是没有加锁)
2.请求与保持   --   我要你的资源这是请求,我还保持我自己的不释放这就是保持,我又要你的资源又不释放我自己的资源,就是请求与保持。
3.不剥夺   --   不能去抢占对方的锁,只能等待对方自动的给你这叫做不剥夺。
4.环路等待条件   --   比如线程A、线程B、线程C。线程A拥有自己的锁它去要线程B的锁,线程B有自己的锁它去要线程C的锁,线程C有自己的锁它去要线程A的锁。形成了一个环路情况。

注意:

只有这四个必要条件同时都满足的情况下才会造成死锁。

如何破坏死锁

概念:

破坏死锁的本质就是破坏这四个条件的至少一个!

做法:

加锁顺序一致
避免锁未释放的场景
资源一次性分配

线程同步

本质

线程运行同步的本质:
当我们在进行临界资源访问安全的前提条件下,让多个线程按照一定的顺序进行资源访问。

先谈生产者消费者模型

生产的过程和消费的过程 - 解耦

生产者跟消费者互不影响。

临时保存产品的场所  --  缓冲区

不符合生产者消费者模型的例子

函数调用:

调用方:生产了数据

形成变量:变量暂时保存数据

目标函数:消费了数据

main函数调用fun函数的时候
正在调用的过程中main正在等待fun函数返回
main函数与fun函数是强耦合关系

"321"原则

3种关系:  生产者和生产者(互斥),消费者和消费者(互斥),生产者和消费者(互斥|[保证共享资源的安全性]/同步)。
-- 产品(数据)

2种角色:  生产者线程,消费者线程

1个交易场所:  一段特定结构的缓冲区。交易的是数据。

注意:
只要我们想写生产消费模型,我们本质工作其实就是维护321原则!

生产者消费者模型的特点

1、生产线程和消费线程进行解耦
2、支持生产和消费的一段时间的忙闲不均的问题 -- 缓冲区的优势
3、提高效率

条件变量

概念

当一个线程互斥地访问某个变量时,它可能发现在其它线程改变状态之前,它什么也做不了。

例如一个线程访问队列时,发现队列为空,它只能等待,只到其它线程将一个节点添加到队列中。这种情况就需要用到条件变量。

定义

是一种数据类型,我们可以使用这个类型来定义变量或者对象。

具体操作 

本质是对线程的PCB做处理。

所用函数

操作系统调用pthread_cond_wait()的时候就是把它放进等待队列里面

操作系统调用pthread_cond_signal()就是它唤醒。

定义的返回值

定义条件变量之前要先初始化。成功返回0,失败返回对应的错误码。

谁排队谁唤醒?

让指定线程在条件变量里面进行排队
主线程进行唤醒。

代码示例:


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

int tickets = 1000;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

void *start_routine(void *args)
{
    std::string name = static_cast<const char *>(args);

    while (true)
    {
        pthread_mutex_lock(&mutex);
        pthread_cond_wait(&cond, &mutex); // 为什么要有mutex 后面会说
        // 判断暂时省略
        std::cout << name << " -> " << tickets << std::endl;
        tickets--;
        pthread_mutex_unlock(&mutex);
    }
    return nullptr;
}

int main()
{
    // 通过条件变量控制线程的执行

    pthread_t t[5];
    for (int i = 0; i < 5; i++)
    {
        char *name = new char[64];
        snprintf(name, 64, "thread %d", i + 1);
        pthread_create(&t[i], nullptr, start_routine, name);
    }

    while (true)
    {
        sleep(1);
        pthread_cond_signal(&cond);
        std::cout << "main thread wakeup one thread" << std::endl;
    }

    for (int i = 0; i < 5; i++)
    {
        pthread_join(t[i], nullptr);
    }

    return 0;
}

 唤醒方式

唤醒单个线程:

int pthread_cond_broadcast(pthread_cond_t *cond);


唤醒多个线程:

int pthread_cond_signal(pthread_cond_t *cond);

运行结果图如下:

Linux——线程的同步与互斥

 

到了这里,关于Linux——线程的同步与互斥的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 手把手教你用python一键抢12306火车票(附代码)

    哈喽,哈喽~,一年一度的抢火车票大战正式拉开序幕… 然饿大多数人碰到的是这种情况:当你满心期待摩拳擦掌准备抢票的时候,你会发现一票难求!想回趟家真难! 那么作为程序猿的你,当然要用程序猿的方式来抢票!下面分享用python来抢票! 城市cookie可根据具体需求自

    2024年02月15日
    浏览(76)
  • 数据结构课程设计之火车票订票系统实现(C语言/C++版本)

    课题描述 编制一个程序,火车票订票的业务活动包括:车次查询、订票、退票、用户管理等。 需求分析 用户信息包括用户姓名、身份证号、用户电话、用户所购列车号、订单号;列车信息包括:列车车站号、车票起点、车票终点、出发时间、到达时间、票价、票数等基本信

    2024年01月19日
    浏览(56)
  • 设计一个亿级高并发系统架构 - 12306火车票核心场景DDD领域建模

    “ 架设一个亿级高并发系统,是多数程序员、架构师的工作目标。 许多的技术从业人员甚至有时会降薪去寻找这样的机会。但并不是所有人都有机会主导,甚至参与这样一个系统。今天我们用12306火车票购票这样一个业务场景来做DDD领域建模。” 要实现软件设计、软件开发

    2024年02月03日
    浏览(51)
  • 基于android studio开发的火车票购票系统app,android移动开发课设,毕业设计

    基于android studio开发实现火车票购票系统app 适用于android移动开发学习项目,课程设计,毕业设计等 开发工具:android studio 或者intellij idea专业版 操作系统:windows10 java: JDK11 构建工具Gradle : gradle-7.0.0 模拟器AVD:pixel 3XL API 30 具体AVD配置详情如下 APP功能 该APP包含17个Activity,每

    2024年02月09日
    浏览(55)
  • [附源码]计算机毕业设计-JAVA火车票订票管理系统-springboot-论-文-ppt

    PPT+论文 本论文主要论述了如何使用JAVA语言开发一个火车订票管理系统 ,本系统将严格按照软件开发流程进行各个阶段的工作,采用B/S架构,面向对象编程思想进行项目开发。在引言中,作者将论述火车订票管理系统的当前背景以及系统开发的目的,后续章节将严格按照软件

    2024年02月12日
    浏览(43)
  • Linux——线程3|线程互斥和同步

    我们上一篇提到过,多个线程执行下面代码可能会出错,具体原因可查看上一篇Linux博客。 为避免这种错误的出现,我们可采用加锁保护。 PTHREAD_MUTEX_INITIALIZER 用pthread_mutex_t定义一把锁。ptherad_mutex_init是对锁进行初始化的函数。如果这把锁是全局的并且是静态定义的,我们可

    2024年02月05日
    浏览(48)
  • 【Linux】线程同步和互斥

    1.临界资源:多线程执行流共享的资源,且一次只能允许一个执行流访问的资源就叫做临界资源。(多线程、多进程打印数据) 2.临界区:每个线程内部,访问临界资源的代码,就叫做临界区。 3.互斥:任何时刻,互斥保证有且只有一个执行流进入临界区,访问临界资源,通常对

    2024年02月08日
    浏览(46)
  • 【Linux】Linux线程互斥与同步

    临界资源:多线程执行流共享的资源就叫做临界资源 临界区:每个线程内部,访问临界资源的代码,就叫做临界区 互斥:任何时刻,互斥保证有且只有一个执行流进入临界区,访问临界资源,通常对临界资源起保护作用 原子性:不会被任何调度机制打断的操作,该操作只有

    2024年02月04日
    浏览(40)
  • Linux——线程的同步与互斥

    目录 模拟抢火车票的过程 代码示例 thread.cc Thread.hpp 运行结果 分析原因 tickets减到-2的本质  解决抢票出错的方案 临界资源的概念 原子性的概念 加锁 定义 初始化 销毁 代码形式如下 代码示例1: 代码示例2: 总结 如何看待锁 申请失败将会阻塞  pthread_mutex_tyrlock 互斥锁实现

    2024年02月06日
    浏览(42)
  • Linux——多线程,互斥与同步

    目录 一.linux互斥 1.进程线程间的互斥相关背景概念 2.互斥量mutex 3.加锁互斥锁mutex 4.锁的底层原理  二.可重入VS线程安全 1.概念 2.常见的线程不安全的情况 3.常见的线程安全的情况  4.常见不可重入的情况  5..常见可重入的情况 6.可重入与线程安全联系  三.死锁 1.死锁四个必

    2024年02月05日
    浏览(33)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包