Linux线程同步实例

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

1. 生产消费者模型基本概念

生产者消费者模型是一种常用的并发设计模式,它可以解决生产者和消费者之间的速度不匹配、解耦、异步等问题。生产者消费者模型的应用场景有很多,例如Excutor任务执行框架、消息中间件activeMQ、任务的处理时间比较长的情况下等。

生产者消费者模型的基本结构如下

  1. 生产者(Producer):负责生成数据或任务,放入缓冲区(Buffer)中。
  2. 消费者(Consumer):负责从缓冲区中取出数据或任务,进行处理。
  3. 缓冲区(Buffer):一般是一个有限大小的队列,用来存储生产者生成的数据或任务,同时提供给消费者使用。

生产者消费者模型的核心是缓冲区,它可以平衡生产者和消费者的处理能力,起到一个数据缓存的作用,同时也达到了一个解耦的作用。缓冲区的实现方式有多种,例如:

  1. 使用Object的wait()和notify()方法,让生产者和消费者在缓冲区满或空时进行等待和唤醒。
  2. 使用Semaphore的acquire()和release()方法,让生产者和消费者通过信号量控制缓冲区的访问。
  3. 使用BlockingQueue阻塞队列,让生产者和消费者通过put()和take()方法自动实现阻塞和唤醒。
  4. 使用Lock和Condition的await()和signal()方法,让生产者和消费者通过条件变量控制缓冲区的状态。
  5. 使用PipedInputStream和PipedOutputStream,让生产者和消费者通过管道流进行通信。

生产者消费者模型的应用场景有很多,例如:

  1. Excutor任务执行框架:通过将任务的提交和任务的执行解耦开来,提交任务的操作相当于生产者,执行任务的操作相当于消费者。
  2. 消息中间件activeMQ: 双十一的时候,会产生大量的订单,那么不可能同时处理那么多的订单,需要将订单放入一个队列里面,然后由专门的线程处理订单。
  3. 任务的处理时间比较长的情况下:比如上传附件并处理,那么这个时候可以将用户上传和处理附件分成两个过程,用一个队列暂时存储用户上传的附件,然后立刻返回用户上传成功,然后有专门的线程处理队列中的附件。

生产者消费者模型优点

  1. 解耦:生产者和消费者之间不直接通信,而是通过缓冲区来进行通信,降低了代码之间的依赖性,简化了工作负载的管理。
  2. 复用:生产者和消费者可以独立地进行复用和扩展,增加了代码的可维护性和可扩展性。
    调整并发数:生产者和消费者的处理速度可能不一致,可以通过调整并发数来平衡速度差异,提高系统的吞吐量和效率。
  3. 异步:生产者不需要等待消费者处理完数据才能继续生产,消费者也不需要等待生产者生成数据才能继续消费,通过异步的方式支持高并发,提高系统的响应性和灵活性。
  4. 支持分布式:生产者和消费者可以运行在不同的机器上,通过分布式的缓冲区来进行通信,增加了系统的可伸缩性和容错性。

2. 基于BlockingQueue的生产者消费者模型

在多线程编程中阻塞队列(Blocking Queue)是一种常用于实现生产者和消费者模型的数据结构。其与普通的队列区别在于,当队列为空时,从队列获取元素的操作将会被阻塞,直到队列中被放入了元素;当队列满时,往队列里存放元素的操作也会被阻塞,直到有元素被从队列中取出(以上的操作都是基于不同的线程来说的,线程在对阻塞队列进程操作时会被阻塞)。

基于BlockingQueue的生产者消费者模型,可以封装一个类,这个类就只有简单的插入、删除操作是一个简单的阻塞队列,并且内部用条件变量来实现。
blockQueue.hpp源代码:

#pragma once
#include <iostream>
#include <queue>
#include <pthread.h>
#include <unistd.h>
using namespace std;

const int gcap = 5;

template <class T>
class BlockQueue
{
public:
    BlockQueue(const int cap = gacp)
    :_cap = cap
    {
        pthread_mutex_init(&mutex, nullptr);
        pthread_cond_init(&_consumerCond, nullptr);
        pthread_cond_init(&_productorCond, nullptr);
    }
    bool isFull()
    {
        return _q.size() == _cap;
    }
    bool isEmpty()
    {
        return _q.empty();
    }
    void push(const T &in)
    {
        pthread_mutex_lock(&_mutex);
        while (isFull())
        {
            pthread_cond_wait(&_productorCond, &_mutex);
            sleep(1); // 每隔1s询问一次队列是否为满,因为消费者可能在这1s中消费
        }
        _q.push(in); // 队列不满,则可以插入
        pthread_cond_signal(&_consumerCond); // 队列某一时刻可能为空,消费者被阻塞,所以需要在这唤醒消费者
        // 线程如果醒着,那么再唤醒不会出问题;相反线程阻塞,再次用函数阻塞也没问题
        pthread_mutex_unlock(&_mutex);
    }
    void pop(T *out)
    {
        pthread_mutex_lock(&mutex);
        while (isEmpty())
        {
            pthread_cond_wait(&_consumerCond, &_mutex);
            sleep(1);
        }
        *out = _q.front();
        _q.pop();
        pthread_cond_signal(&_productorCond);
        pthread_mutex_unlock(&_mutex);
    }
    ~BlockQueue()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_consumerCond);
        pthread_cond_destroy(&_productorCond);
    }
private:
    queue<T> _q;
    int _cap;
    pthread_mutex_t _mutex;
    pthread_cond_t _consumerCond;  // 消费者对应的条件变量,若空,则wait
    pthread_cond_t _productorCond; // 生产者对应的条件变量,若满,则wait
};

然后就可以实现简单的单生产单消费以及多生产多消费的样例,main文件中的代码如下:

#include "blockQueue.hpp"
#include <time.h>

void *productor(void *args)
{
    BlockQueue<int> *q = static_cast<BlockQueue<int>*>(args);
    int count = 20;
    while (count--)
    {
        int val = rand() % 5 + 1;
        cout << "生产的数据:" << val << endl;
        q->push(val);
    }
    return nullptr;
}
void *consumer(void* args)
{
    BlockQueue<int> *q = static_cast<BlockQueue<int>*>(args);
    while (true)
    {
        int val = 0;
        q->pop(&val);
        cout << "消费的数据:" << val << endl;
        usleep(300);
        if (q->isEmpty())
            break;
    }
    return nullptr;
}
// 单生产单消费
int main()
{
    srand((unsigned int)time(0)); //随机数种子

    BlockQueue<int> *q = new BlockQueue<int>;
    pthread_t c, p;
    pthread_create(&c, nullptr, consumer, q);
    pthread_create(&p, nullptr, productor, q);

    while (true)
    {
        sleep(1);
        if (q->isEmpty())
            break;
    }

    pthread_join(c, nullptr);
    pthread_join(p, nullptr);
    delete q;

    return 0;
}

//多生产多消费
// int main()
// {
//     srand((uint64_t)time(nullptr));
//     // BlockQueue<int> *bq = new BlockQueue<int>();
//     BlockQueue<int> *bq = new BlockQueue<int>();
//     // 单生产和单消费 -> 多生产和多消费
//     pthread_t c[2], p[3];
//     pthread_create(&c[0], nullptr, consumer, bq);
//     pthread_create(&c[1], nullptr, consumer, bq);
//     pthread_create(&p[0], nullptr, productor, bq);
//     pthread_create(&p[1], nullptr, productor, bq);
//     pthread_create(&p[2], nullptr, productor, bq);

//     pthread_join(c[0], nullptr);
//     pthread_join(c[1], nullptr);
//     pthread_join(p[0], nullptr);
//     pthread_join(p[1], nullptr);
//     pthread_join(p[2], nullptr);
//     delete bq;
//     return 0;
// }

运行结果如下:
Linux线程同步实例,Linux学习笔记,linux
基于BlockingQueue的生产者消费者模型是一种常见的多线程设计模式,它有以下几个优点:

  1. 简化编程:BlockingQueue提供了线程安全的入队和出队操作,无需自己实现同步和锁机制,降低了编程难度和出错风险。
  2. 提高性能:BlockingQueue支持阻塞和超时机制,可以根据队列的状态自动调整生产者和消费者的状态,避免了无效的等待和轮询,提高了系统的吞吐量和响应速度。
  3. 增强可扩展性:BlockingQueue可以作为有界队列或无界队列使用,可以根据实际需求调整队列的容量和策略,增加了系统的灵活性和可扩展性。

3. 基于环形队列的生产消费模型

环形队列是一种特殊的队列,它是在队列的基础上添加了一些限制条件,使得队列可以在固定大小的存储空间下进行循环使用。环形队列可以用数组实现,数组中的元素按照一定的顺序排列,并且当队列头或者队列尾指针到达数组的尾部时,会自动从数组的头部开始重新循环使用。环形队列的一个好处是,当队列满时,可以通过覆盖队列头部的元素来继续存储新的元素,这样可以使得队列在一定程度上具有循环使用的能力,节省存储空间。但是在使用环形队列时需要注意一些细节问题,比如队列空、队列满、队列大小等等。
Linux线程同步实例,Linux学习笔记,linux
环形结构起始状态和结束状态都是一样的,不好判断为空或者为满,所以可以通过加计数器或者标记位来判断满或者空。另外也可以预留一个空的位置,作为满的状态。但是现在有信号量这个计数器,就很简单的进行多线程间的同步过程,所以环形队列的生产消费者模型内部使用信号量来实现。
基于环形队列的生产消费模型的代码如下:

#pragma once

#include <iostream>
#include <vector>
#include <pthread.h>
#include <semaphore.h>
using namespace std;

template <class T>
class RingQueue
{
public:
    RingQueue(int num = N)
    :_ring(num)
    ,_cap(num)
    {
        sem_init(&_data_sem, 0, 0);
        sem_init(&_space_sem, 0, _cap);
        _c_step = _p_step = 0;

        pthread_mutex_init(&_c_mutex,nullptr);
        pthread_mutex_init(&_p_mutex,nullptr);
    }
    // 生产
    void push(const T &in)
    {
        P(_space_sem);
        Lock(_p_mutex);
        _ring[_p_step++] = in;
        _p_step %= _cap;
        Unlock(_p_mutex);
        V(_data_sem);
    }
    // 消费
    void pop(T &out)
    {
        P(_data_sem);
        Lock(_c_mutex);
        out = _ring[_c_step++];
        _c_step %= _cap;
        Unlock(_c_mutex);
        V(_space_sem);
    }
    ~RingQueue()
    {
        sem_destroy(&_space_sem);
        sem_destroy(&_data_sem);

        pthread_mutex_destroy(&_c_mutex);
        pthread_mutex_destroy(&_p_mutex);
    }
private:
    void P(sem_t &sem)
    {
        sem_wait(&sem);
    }
    void V(sem_t &sem)
    {
        sem_post(&sem);
    }
    void Lock(pthread_mutex_t &mutex)
    {
        pthread_mutex_lock(&mutex);
    }
    void Unlock(pthread_mutex_t &mutex)
    {
        pthread_mutex_unlock(&mutex);
    }
private:
    vector<T> _ring;
    int _cap;         // 环形队列容器大小
    sem_t _data_sem;  // 表示数据量的信号量,只有消费者关心
    sem_t _space_sem; // 表示空间量的信号量,只有生产者关心
    int _c_step;      // 环形队列中消费的位置
    int _p_step;      // 环形队列中生产的位置

    pthread_mutex_t _c_mutex;
    pthread_mutex_t _p_mutex;
};

然后生产一些任务,这些任务不弄简单的随机数,而弄一些随机出来的加减乘除运算,所以再封装一个类,这个类可以做加减乘除运算,该类的代码如下:

#pragma once

#include <iostream>
using namespace std;

class Task
{
public:
    Task()
    {}
    Task(const int x, const int y, const char op)
    :_x(x)
    ,_y(y)
    ,_op(op)
    ,_exitCode(0)
    {}
    void operator()()
    {
        switch(_op)
        {
            case '+':
                _result = _x + _y;
                break;
            case '-':
                _result = _x - _y;
                break;
            case '*':
                _result = _x * _y;
                break;
            case '/':
                if (_y == 0)
                    _exitCode = -1;
                else
                    _result = _x / _y;
                break;
            case '%':
                _result = _x % _y;
                break;
            default:
                break;
        }
    }
    string formatArg() // 输入的格式
    {
        return to_string(_x) + _op +to_string(_y) + '=';
    }
    string formatRes() // 输出的格式
    {
        return to_string(_result) + '(' + to_string(_exitCode) + ')';
    }
    ~Task()
    {}
private:
    int _x;
    int _y;
    char _op;

    int _result;
    int _exitCode;
};

然后直接用多生产多消费进行验证,多生产多消费的main.cc文件代码如下:

#include "RingQueue.hpp"
#include "task.hpp"
#include <ctime>
#include <pthread.h>
#include <memory>
#include <sys/types.h>
#include <unistd.h>
#include <cstring>

using namespace std;

const char *ops = "+-*/%";

void *consumerRoutine(void *args)
{
    RingQueue<Task> *rq = static_cast<RingQueue<Task> *>(args);
    while (true)
    {
        Task t;
        rq->pop(t);
        t();
        cout << "consumer done, 处理完成的任务是: " << t.formatRes() << endl;
    }
}

void *productorRoutine(void *args)
{
    RingQueue<Task> *rq = static_cast<RingQueue<Task> *>(args);
    while (true)
    {
        sleep(1);
        int x = rand() % 100;
        int y = rand() % 100;
        char op = ops[(x + y) % strlen(ops)];
        Task t(x, y, op);
        rq->push(t);
        cout << "productor done, 生产的任务是: " << t.formatArg() << endl;
    }
}

int main()
{
    srand(time(nullptr));
    RingQueue<Task> *rq = new RingQueue<Task>();
    // 单生产单消费
    // pthread_t c, p;
    // pthread_create(&c, nullptr, consumerRoutine, rq);
    // pthread_create(&p, nullptr, productorRoutine, rq);

    // pthread_join(c, nullptr);
    // pthread_join(p, nullptr);

    pthread_t c[3], p[2];
    for (int i = 0; i < 3; i++)
        pthread_create(c + i, nullptr, consumerRoutine, rq);
    for (int i = 0; i < 2; i++)
        pthread_create(p + i, nullptr, productorRoutine, rq);

    for (int i = 0; i < 3; i++)

        pthread_join(c[i], nullptr);
    for (int i = 0; i < 2; i++)

        pthread_join(p[i], nullptr);

    delete rq;
    return 0;
}

最后就会有如下形式的任务被生产者派发,然后由消费者处理问题。
Linux线程同步实例,Linux学习笔记,linux
基于环形队列的生产消费模型是一种常见的并发同步模式,它有以下优缺点:
优点:

  1. 解耦:生产者和消费者不直接交互,而是通过环形队列进行数据传递,降低了两者之间的耦合度。
  2. 支持并发:生产者和消费者可以同时访问环形队列的不同位置,提高了并发性能。
  3. 支持忙闲不均:当生产者和消费者的速度不匹配时,环形队列可以缓冲数据,避免数据丢失或阻塞。

缺点:

  1. 需要额外的空间:环形队列需要预先分配固定大小的空间,可能造成空间浪费或不足。
  2. 需要额外的同步机制:环形队列需要使用信号量或其他同步机制来控制生产者和消费者之间的协作,增加了编程复杂度。
  3. 可能出现饥饿或饱和:当环形队列满或空时,生产者或消费者可能会长时间等待,影响系统的响应性。

4. 线程池

Linux线程池是一种管理多个线程的技术,它可以提高程序的性能和资源利用率。Linux线程池的基本思想是:

  1. 预先创建一定数量的线程,放在一个池中,这些线程称为核心线程。
  2. 当有新的任务到来时,如果有空闲的核心线程,就分配给它执行;如果没有空闲的核心线程,就将任务放在一个任务队列中,等待有空闲的线程来执行。
  3. 如果任务队列也满了,就创建新的线程,超过核心线程数量的线程称为非核心线程。
  4. 如果非核心线程空闲时间超过一定的限制,就销毁这些线程,回收资源。
  5. 如果核心线程空闲时间超过一定的限制,并且设置了允许回收核心线程的标志,就销毁这些线程,回收资源。

Linux线程池的优点有:

  1. 降低创建和销毁线程的开销,提高响应速度。
  2. 控制并发的数量,避免过多的线程竞争,提高系统稳定性。
  3. 统一管理和调度线程,提高代码的可维护性。

Linux线程池的实现方法有:

  1. 使用POSIX标准提供的pthread库来创建和管理线程,使用互斥锁和条件变量来实现任务队列和同步机制。
  2. 使用C++标准库中的std::thread类来创建和管理线程,使用std::queue容器来实现任务队列,使用std::mutex和std::condition_variable来实现同步机制。
  3. 使用第三方库或框架来实现线程池,例如Boost.Asio、libevent、libuv等。

对于上述所描述的,可以使用互斥锁和条件变量来实现任务队列和同步机制,实现简单的线程池,代码如下:

#pragma once

#include <iostream>
#include <queue>
#include <vector>
#include <pthread.h>
#include <unistd.h>
#include "task.hpp"
using namespace std;

const static int N = 5;

template <class T>
class ThreadPool
{
public:
    ThreadPool(int num = N)
    :_num(num)
    ,_threads(num)
    {
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_cond, nullptr);
    }
    void lockQueue()
    {
        pthread_mutex_lock(&_mutex);
    }
    void unlockQueue()
    {
        pthread_mutex_unlock(&_mutex);
    }
    void threadWait()
    {
        pthread_cond_wait(&_cond, &_mutex);
    }
    void threadWakeup()
    {
        pthread_cond_signal(&_cond);
    }
    bool isEmpty()
    {
        return _tasks.empty();
    }
    T popTask()
    {
        T t = _tasks.front();
        _tasks.pop();
        return t;
    }
    void pushTask(const T &t)
    {
        lockQueue();
        _tasks.push(t);
        threadWakeup(); // 插入任务后唤醒线程处理任务
        unlockQueue();
    }
    static void *threadRoutine(void *args) // 对于类的内部成员函数,会有默认的this指针,所以可以将这个函数定义在类的外部,
                                           // 或者定义静态成员函数,但静态成员函数不能直接访问类的内部成员。
    {
        pthread_detach(pthread_self()); // 线程分离,这样子线程就可以自己释放自己的资源
        ThreadPool<T> *tp = static_cast<ThreadPool<T>*>(args); 
        // 对于tp指针来说,他不能访问私有成员,所以可以用一些函数去访问类的私有成员,或者将私有成员暴露出来,属性设置为public
        while (true)
        {
            // 检测有没有任务
            tp->lockQueue();
            while (tp->isEmpty())
            {
                tp->threadWait();
            }
            T t = tp->popTask(); // 拿出队列中的任务
            tp->unlockQueue();

            //test:放入一些数据
            t(); // task任务内部是用该仿函数来处理任务的
            cout << "thread handler done, result: " << t.formatRes() << std::endl;
        }
    }
    void start()
    {
        for (int i = 0; i < _num; ++i)
        {
            pthread_create(&_threads[i], nullptr, threadRoutine, this);
        }
    }
    ~ThreadPool()
    {
        pthread_cond_destroy(&_cond);
        pthread_mutex_destroy(&_mutex);
    }
private:
    vector<pthread_t> _threads;
    int _num;

    queue<T> _tasks;

    pthread_mutex_t _mutex;  // 使用互斥锁和条件变量来实现任务队列和同步机制
    pthread_cond_t _cond;
};

#include <memory>

int main()
{
    unique_ptr<ThreadPool<Task>> tp(new ThreadPool<Task>());
    tp->start();
    while (true)
    {
        int x, y;
        char op;
        cout << "please Enter x> ";
        cin >> x;
        cout << "please Enter y> ";
        cin >> y;
        cout << "please Enter op(+-*/%)> ";
        cin >> op;
        Task t(x, y, op);
        tp->pushTask(t);
        usleep(500);
    }
    return 0;
}

当有任务时,该线程池会处理任务,没有任务是则会等待。
Linux线程同步实例,Linux学习笔记,linux文章来源地址https://www.toymoban.com/news/detail-723417.html

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

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

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

相关文章

  • 【Linux】Linux线程互斥与同步

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

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

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

    2024年02月05日
    浏览(47)
  • Linux和windows进程同步与线程同步那些事儿(五):Linux下进程同步

    Linux和windows进程同步与线程同步那些事儿(一) Linux和windows进程同步与线程同步那些事儿(二): windows线程同步详解示例 Linux和windows进程同步与线程同步那些事儿(三): Linux线程同步详解示例 Linux和windows进程同步与线程同步那些事儿(四):windows 下进程同步 Linux和wi

    2024年02月02日
    浏览(39)
  • Linux--线程-条件控制实现线程的同步

    1.条件变量 条件变量是线程另一可用的同步机制。条件变量给多个线程提供了一个会合的场所。条件变量与互斥量一起使用时,允许线程以无竞争的方式等待特定的条件发生。 条件本身是由互斥量保护的。线程在改变条件状态前必须首先锁住互斥量,其他线程在获得互斥量之

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

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

    2024年02月08日
    浏览(45)
  • 【Linux】线程同步

    初始化条件变量-pthread_cond_init 初始化条件变量的函数叫做 pthread_cond_init 参数说明 cond:需要初始化的条件变量 attr:初始化条件变量的属性,一般设置为NULL 返回值说明 初始化成功返回0,失败返回错误码 注意:调用 pthread_cond_init 函数初始化条件变量叫做动态分配,我们还可以用静

    2024年02月07日
    浏览(33)
  • Linux->线程同步

    前言: 1 线程同步引入 2 条件变量 2.1 线程饥饿 2.2 条件变量接口 2.3 添加条件变量 3 生产者和消费者模型         本篇主要讲解了关于线程同步的相关知识,还有生产者和消费者模型的认识和使用。         在讲解线程同步之前,我们先来看一下当一个程序之中只有线程互

    2024年02月11日
    浏览(32)
  • linux:线程同步

    个人主页 : 个人主页 个人专栏 : 《数据结构》 《C语言》《C++》《Linux》 本文作为我对于线程同步知识总结 同步:在保证数据安全的前提下,让线程能够按照某种顺序访问临界资源,从而有效避免饥饿问题,叫做同步 竞态条件:是指多个线程同时访问系统共享资源时,由

    2024年04月17日
    浏览(24)
  • 线程的同步和互斥学习笔记

    目录 互斥锁的概念和使用  线程通信-互斥  互斥锁的创建和销毁  申请锁-pthread_mutex_lock  释放锁-pthread_mutex_unlock 读写锁的概念和使用 死锁的避免 线程通信-互斥 临界资源 一次只允许一个任务(进程、线程)访问的共享资源 概念:         不能同时访问的资源,比如写

    2024年01月25日
    浏览(35)
  • 【Linux】多线程2——线程互斥与同步/多线程应用

    💭上文主要介绍了多线程之间的独立资源,本文将详细介绍多线程之间的 共享资源 存在的问题和解决方法。 intro 多线程共享进程地址空间,包括创建的全局变量、堆、动态库等。下面是基于全局变量实现的一个多线程抢票的demo。 发现错误:线程抢到负数编号的票,为什么

    2024年02月10日
    浏览(42)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包