Linux多进程篇【3】——同步

Linux进程间共享内存通信时如何同步?(附源码) 然后,程序使用互斥量和条件变量来同步进程之间的访问共享内存区域。需要注意的是,在使用条件变量时,我们需要遵循一些规则来保证程序的正确性,如在等待条件变量时必须锁定互斥量,并使用while循环来检查条件变量的值是否满足要求,等待条件变量信号的线程必须在等待之前锁定互斥量,在等待之后解锁互斥量,等待条件变量信号的线程可能会因为接收到信号而提前返回等等。总之,使用互斥量和条件变量来实现进程间共享内存通信的同步,需要我们仔细考虑程序中所有可能出现的情况,并正确地使用互斥量和条件变量函数来同步进程之间的访问。 阅读详情

同步

在使用互斥时我们会发现一个问题,如果一个线程频繁地使用互斥锁来占用临界资源,会严重影响别的线程的效率,而同步就是在保证数据安全的情况下让多线程的执行具有一定的顺序性,以此提高运行效率。

条件变量

创建条件变量跟创建互斥锁类似,在全局定义一个pthread_cond_t类型的变量即可。

接口

初始化
  • 动态初始化
pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);
  • 静态初始化
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
销毁
int pthread_cond_destroy(pthread_cond_t *cond);
等待条件满足
int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);
唤醒等待
int pthread_cond_broadcast(pthread_cond_t *cond); // 唤醒所有线程
int pthread_cond_signal(pthread_cond_t *cond); // 唤醒某个进程(阻塞队列中的第一个)

案例

在这个例子共创建了两类进程,第一类是一个控制进程(master),第二类是三个工作进程(worker),master每秒释放一次一个信号让其中一个worker工作

const int NUM = 3;
pthread_mutex_t mtx;
pthread_cond_t cond;

void* ctrl(void* args)
{
    string name = (char*)args;
    while (true)
    {
        // 唤醒在条件变量下等待的一个线程
        // 在条件变量等待队列中的第一个线程
        pthread_cond_signal(&cond);
        sleep(1);
    }
}

void* work(void* args)
{
    int number = *(int*)args;
    delete (int*)args;
    while (true)
    {
        pthread_cond_wait(&cond, &mtx);
        cout << "worker: " << number << " is working···" << endl;
    }
}

int main()
{
    pthread_mutex_init(&mtx, nullptr);
    pthread_cond_init(&cond, nullptr);
    pthread_t master;
    pthread_t worker[NUM];
    pthread_create(&master, nullptr, ctrl, (void*)"master");
    for (int i = 0; i < NUM; i++)
    {
        int *number = new int(i);
        pthread_create(worker + i, nullptr, work, (void*)number);
    }
    for (int i = 0; i < NUM; i++)
    {
        pthread_join(worker[i], nullptr);
    }
    pthread_join(master, nullptr);
    pthread_mutex_destroy(&mtx);
    pthread_cond_destroy(&cond);
    return 0;
}

在这里插入图片描述

从运行情况中我们会发现worker每次被唤醒是按照一定顺序的,从此我们可以推断出其实条件变量内部存在一个等待队列

生产者消费者模型

让我们来思考一下去超市买东西的例子,我们去超市买的东西其实是超市从供货商进的,那么为什么消费者不能自己去供货商买东西呢,供货商为什么不能自己承担销售的任务呢?归根结底为的是减少交易成本和提高效率,因为供货商往往在比较偏僻的地方,消费者不希望每次购物都跑这么远的路;而对于供货商来说,他们每天会生产大量的货物,消费者每次购买商品的数量往往是很小的,这样的销售不但麻烦而且销量也远远不能达到供货商的预期,不同于普通消费者,超市具有收集需求,他每次都会进大量的货,供货商只需进行几次交易就可以出售大量的商品。另外,当供货商的生产出现问题时,因为超市内有大量的存货,因此在短期内消费者的消费并不会收到影响,也就是说超市将生产消费环节进行了“解耦”,这也是提高效率的一个方面。

  • 供货商 vs 供货商:竞争 互斥
  • 消费者 vs 消费者:竞争 互斥
  • 供货商 vs 消费者:互斥 同步
    在这个模型里包含的只有生产者、消费者这2个角色间的这3种关系,超市只是一个交易场所(临界资源)。

BlockingQueue

现在模拟实现一个BlockingQueue

namespace ssj_blockqueue
{
    const int default_cap = 5;

    template <class T>
    class BlockQueue
    {
    private:
    queue<T> _bq;
    int _cap;
     pthread_mutex_t _mtx;
    // 当生产满了的时候,就应该不要生产了(不要竞争锁了),而应该让消费者来消费
    // 当消费空了,就不应该消费(竞争锁),应该让生产者来进行生产
    pthread_cond_t _is_full; // _bq满,消费者在该条件变量下等待
    pthread_cond_t _is_empty; // _bq满,消费者在该条件变量下等待

private:
    bool IsFull()
    {
        return _bq.size() == _cap;
    }

    bool IsEmpty()
    {
        return _bq.size() == 0;
    }

    void LockQueue()
    {
        pthread_mutex_lock(&_mtx);
    }

    void UnlockQueue()
    {
        pthread_mutex_unlock(&_mtx);
    }

    void ProducterWait()
    {
        // pthread_cond_wait
        //1. 调用的时候,会首先自动释放_mtx,然后再挂起自己
        //2. 返回的时候,会首先自动竞争锁,获取到锁之后,才能返回
        pthread_cond_wait(&_is_empty, &_mtx);
    }

    void ConsumerWait()
    {
        pthread_cond_wait(&_is_full, &_mtx);
    }

    void WakeupConsumer()
    {
        pthread_cond_signal(&_is_full);
    }

    void WakeupProducter()
    {
        pthread_cond_signal(&_is_empty);
    }
    public:
        BlockQueue(int cap = default_cap)
        : _cap(cap)
        {
            pthread_mutex_init(&_mtx, nullptr);
            pthread_cond_init(&_is_empty, nullptr);
            pthread_cond_init(&_is_full, nullptr);
        }

        ~BlockQueue()
        {
            pthread_mutex_destroy(&_mtx);
            pthread_cond_destroy(&_is_empty);
            pthread_cond_destroy(&_is_full);
        }
    public:
        void Push(const T &in)
        {
            LockQueue();
            while (IsFull()) // 使用循环避免伪唤醒和挂起失败
            {
                ProducterWait();
            }

            _bq.push(in);

            if (_bq.size() > _cap / 2) WakeupConsumer();
            UnlockQueue();
        }

        void Pop(T *out)
        {
            LockQueue();
            while (IsEmpty())
            {
                ConsumerWait();
            }
            *out = _bq.front();
            _bq.pop();
            if (_bq.size() < _cap / 2) WakeupProducter();
            UnlockQueue();
        }
    };
}
Linux内核:进程管理——进程间同步与互斥 进程的并发性带来了异步性(各个并发的进程独立的以不可预知的速度向前推进),而有的进程则需要有次序的相互配合来完成作业,所以有了进程同步。进程同步:在多道程序环境下,进程是并发执行的,不同进程之间存在着不同的相互制约关系,。为了协调进程之间的相互制约关系,引入了进程同步的概念。临界资源虽然多个进程可以共享系统中的各种资源,但其中许多资源一段时间内只能为一个进程所使用,我们把一次仅允许一个进程使用的资源称为临界资源。许多物理设备都属于临界资源,如打印机等。 阅读详情

相关推荐

Python多进程同步——文件锁

Python多进程同步——文件锁

weixin_45001971的博客 8803

Linux下的多进程和多线程互斥同步示例

为了确保共享资源的正确访问,我们需要使用互斥和同步机制。本文将为您提供一个简单的例子,展示如何在Linux中实现多进程和多线程的互斥同步。通过使用互斥锁和条件变量,您可以确保共享资源的正确访问,并避免竞态条件和数据不一致的问题。这是通过互斥锁和条件变量的同步机制来实现的,确保每个线程按照正确的顺序访问和更新计数器。我们将创建一个包含共享资源的简单计数器,并使用互斥锁和条件变量来实现多进程和多线程之间的同步。一旦条件满足,线程会执行计数器的增加操作,并打印当前计数器的值。来实现线程之间的同步

CodeNexus的博客 248

【python3高级编程】一文搞懂多进程

1 Linux平台下的多进程 1.1进程: 指正在执行的程序,是程序正在运行的一个实例,由程序指令和从文件、其他程序中读取的数据或者系统用户的输入组成。 1.2线程和进程的关系: 1.对操作系统来说,一个任务就是一个进程, 2.在一个进程内部,要同时做很多件,就需要同时运行多个“子任务”,把进程内部的这些子任务成为线程 3.线程是最小的执行单元,一个线程只能属于一个进程,而一个进程可以由很多线程,但是进程至少有一个线程组成,这个线程成为主线程 os.getpid()函数:获取Py...

qq_36242554的博客 1106

多进程、多线程同步(通讯)的方法

进程间通讯: 管道( pipe ):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。 有名管道 (namedpipe) : 有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。 高级管道(popen):将另一个程序当做一个新的进程在当前程序进程中启动,则它算是当前程序的子进程,这种方式我们成为高级管道方式。 信号量( semophore ) :信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一

weixin_44226857的博客 5820

Linux进程管理之进程同步

并发进程存在的问题 系统当中的多个进程,从资源访问的角度来看,一个进程的运行,有没有可能受到其他进程的制约。有可能 一个和时间有关的错误 共享变量的修改冲突一竞争关系 进程之间的同步关系 进程间的制约关系 (1)竞争关系:有些资源需要互斥使用,因此各进程竞争使用这些资源一独占分配到的部分或全部共享资源,进程的这种关系为进程的互斥 (2)同步关系:系统中多个进程中发生的事件存在某种时序关系,需要相互合作,共同完成一项任务。即一个进程运行到某一点时要求另一伙伴进程为它提供消息,在未获得消息之前该进程处于等待

weixin_46048542的博客 2514

Linux编程实现临界资源管理,操作系统:编程模拟多进程共享临界资源linux,c语言教程.docx...

操作系统:编程模拟多进程共享临界资源linux,c语言教程一、课程设计目的本课程设计是学生学习完《计算机操作系统》课程后,进行的一次全面的综合训练,通过课程设计,让学生更好地掌握操作系统的原理及实现方法,加深对操作系统基础理论和重要算法的理解,加强学生的动手能力。二、课程设计要求从课程设计的目的出发,通过设计工作的各个环节,达到以下教学要求:每位同学从所给题目中任选一个(如自拟题目,需经教师同意)...

weixin_28810315的博客 426

【嵌入式软件工程师面经】Linux多进程与多线程

准备多线程和多进程的面试问题时,深入理解每个问题的核心概念以及它们在实际系统中的应用是非常重要的。实际场景中的问题通常比理论上更为复杂,因此理解如何在特定的上下文中应用这些概念至关重要,当准备这类面试问题时,最好的方法是结合理论知识与实践经验,尝试举一些实际应用中的例子来展示你如何解决多线程和多进程相关的问题。此外,理解Linux内核的工作原理和相关的系统调用也非常重要

嵌入式技术开发 2228

Linux多进程实现生产者消费者问题

1. 任务简介 生产者消费者问题(Producer-consumer problem),也称有限缓冲问题(Bounded-buffer problem),是一个著名的进程同步问题的经典案例。它描述的是有一组生产者进程在生产产品,并将这些产品提供给一组消费者进程去消费。为使生产者进程和消费者进程能够并发执行,在这两者之间设置里一个具有nnn个缓冲区的缓冲池,生产者进程将他所生产的的产品放入一个缓冲区中;消费者进程可从一个缓冲区中取走产品并进行消费。尽管所有的生产者进程和消费者进程都是以异步方式运行的,但亡们之

BrilliantAntonio的博客 1万+

linux 多进程 同步,Linux多进程多线程互斥同步例子

Linux多进程多线程互斥同步例子,运行顺序:先运行进程1,再运行进程2。进程1#include #include #include #include #include #include #define DEBUG 1#define SHARE_KEY 0x1234#define THREAD_NUM 4typedef struct{pthread_mutex_t lock;pthread_con...

weixin_36096321的博客 207

linux 多进程 同步,linux 多进程多线程及其同步

Linux 多进程、多线程及网络编程指南1、进程(多进程)2、线程(多线程)3、进程间通信4、并发程序的同步5、s ocket网络编程1、进程1.1进程定义进程是程序在计算机上的执行活动,是一个运行着一个或多个线程的地址空间和这些线程所需要的系统资源,其中包括程序代码、数据、变量、打开文件的文件描述符、和环境。1.2进程的五种状态在五状态进程模型中,进程状态被分成下列五种状态。进程在运行过程中主要...

weixin_34357833的博客 219

Linux 多进程详解

每个进程拥有独立的地址空间和系统资源,通过进程间通信(IPC)进行协作。掌握 Linux 多进程技术是系统级开发的必备技能,合理使用可显著提升程序性能和可靠性。是程序的执行实例,包含代码、数据、内存空间、文件描述符、环境变量等资源。:将不稳定模块(如第三方插件)运行在独立进程,避免主程序崩溃。:Linux 优化机制,父子进程共享内存,直到修改时才复制。:一个或多个进程组的集合,通常由 Shell 创建。创建的进程形成父子关系,父进程监控子进程状态。:一组相关进程的集合,共享同一 PGID。

m0_56508437的博客 996

Python3 多进程 使用情况

Python3 多进程 (multiprocessing) 要让Python程序实现多进程(multiprocessing),我们先了解操作系统的相关知识。 Unix/Linux操作系统提供了一个fork()系统调用,它非常特殊。普通的函数调用,调用一次,返回一次,但是fork()调用一次,返回两次,因为操作系统自动把当前进程(称为父进程)复制了一份(称为子进程),然后,分别在父进程和子进程内返回...

BBBrian的博客 462

linux c++ 多进程_Linux多进程编程入门(代码)

源自CSDNCSDN-专业IT技术社区-登录​blog.csdn.net一、基础知识1.1、进程的概念一个程序文件, 只是一堆待执行的代码和部分待处理的数据,它们只有被加载到内存中,然后让CPU逐条执行其代码,根据代码做出相应的动作,才形成一个真正“活的”、动态的进程(Process)因此, 进程是一个动态变化的过程,是一出有始有终的戏,而程序文件只是这一系列动作的原始蓝本,是一个静态的剧本进程就...

weixin_36462532的博客 2509

linux shell多进程

ls

scorpion_zs的专栏 2263

linux多进程开发详解

多进程的最经典的开发场景就是Nginx,这是因为多进程很适合做负载均衡,进程能够申请资源,而线程更多的则是资源共享。

weixin_42295969的博客 1016

Linux编程:多进程同步-文件锁

另外需要注意的是:加锁时,进程必须对文件有相应的文件访问权限,即加读锁,该文件必须是读打开,加写锁时,该文件必须是写打开。文件锁又叫记录锁,他的作用是:当一个进程正在读或修改文件的某个部分是,可以通过文件锁阻止其他进程修改同一文件区。不仅仅是文件,对于多进程间共享的资源,都可以通过文件锁进行同步。加锁的进程结束后会自动释放锁。

风静如云的博客 1280

多进程和多线程区别以及优缺点

多进程和多线程主要区别是:线程是进程的子集,一个进程可能由多个线程组成。多进程的数据是分开的、共享复杂,需要用IPC,但同步简单;多线程共享进程数据、共享简单,但同步复杂。 多进程,window应用程序中消息有两种送出途径:直接和排队。Windows或某些运行的应用程序可直接发布消息给窗口过程,消息可送到消息列象连续不断轮询消息列队的OS中当前执行的每个进程,事件驱动不是由事件的顺序来控制的,而是由事件的发生来控,而事件的发生是随机的、不确定的,这就允许程序的用户用各种合理的顺序来安排程序的流程。 多线

weixin_63956681的博客 1万+

Linux :并发和并行,多线程和多进程同步和异步的关系

1.进程、线程 (1)进程概念 进程是一个具有一定独立功能的程序在一个数据集上的一次动态执行的过程,是操作系统进行资源分配和调度的一个独立单位,是应用程序运行的载体。进程是一种抽象的概念,从来没有统一的标准定义。进程一般由程序,数据集合和进程控制块三部分组成。程序用于描述进程要完成的功能,是控制进程执行的指令集;数据集合是程序在执行时所需要的数据和工作区;程序控制块包含进程的描述信息和控制信息...

du_lijun的博客 1431
上一篇: Linux多线程篇【2】——互斥锁
下一篇: Linux多进程篇【4】——POSIX信号量
JayceSun449
博客等级 码龄5年 554粉丝 · 49原创
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JayceSun449

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值