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日
    浏览(72)
  • 数据结构课程设计之火车票订票系统实现(C语言/C++版本)

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

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

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

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

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

    2024年02月12日
    浏览(39)
  • 基于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日
    浏览(52)
  • Linux——线程3|线程互斥和同步

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

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

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

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

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

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

    互斥 指的是一种机制,用于确保在同一时刻只有一个进程或线程能够访问共享资源或执行临界区代码。 互斥的目的是 防止多个并发执行的进程或线程访问共享资源时产生竞争条件,从而保证数据的一致性和正确性 ,下面我们来使用多线程来模拟实现一个抢票的场景,看看所

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

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

    2024年02月05日
    浏览(32)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包