线程与多线程(二)

线程与多线程(二)

  • 一、线程互斥
    • 1、相关概念
  • 二、互斥锁
    • 1、介绍
    • 2、使用场景
    • 3、初始化
      • (1)函数
      • (2)概念
    • 4、销毁
      • (1)函数
      • (2)概念
    • 5、加锁
      • (1)函数
      • (2)概念
    • 6、解锁
      • (1)函数
      • (2)概念
    • 7、示例代码
    • 8、运行结果
  • 三、安全
    • 1、线程安全
    • 2、重入
    • 3、常见的线程不安全的情况
  • 四、死锁
    • 1、概念
    • 2、产生的必要条件
    • 3、避免方法
  • 五、条件变量
    • 1、背景概念
    • 2、初始化与销毁
    • 3、等待
      • (1)函数
      • (2)参数意义
      • (3)互斥锁的作用
    • 4、唤醒
      • (1)函数
      • (2)概念
    • 5、示例代码
    • 6、运行结果
  • 六、生产者消费者模型
    • 1、概念
    • 2、示意图
    • 3、优点
    • 4、环形队列
      • (1)示意图
      • (2)说明
  • 七、POSIX信号量
    • 1、概念
    • 2、初始化
      • (1)函数
      • (2)概念
    • 3、销毁
      • (1)函数
      • (2)概念
    • 4、等待
      • (1)函数
      • (2)概念
    • 5、发布
      • (1)函数
      • (2)概念
  • 八、单例模式
    • 1、概念
    • 2、实现方式
      • (1)饿汉模式
      • (2)懒汉模式
  • 九、其他常见的锁

本文为多线程相关的内容,线程相关的内容参见线程与多线程(一)。

一、线程互斥

1、相关概念

  • 临界资源(互斥资源)为多线程执行流共享的资源。
  • 临界区为每个线程内部,访问临界资源的程序段(代码)。
  • 互斥为任何时刻都保证有且只有一个执行流进入临界区访问临界资源,通常对临界资源起保护作用。
  • 原子性为所进行的操作不会被任何调度机制打断。该操作只有两态,即完成态和未完成态,而没有中间状态。

二、互斥锁

1、介绍

  • 使用互斥锁的本质是用时间来换取安全,表现为线程对于临界区代码串行执行,原则上是尽量的要保证临界区代码越少越好。
  • 因为锁本身就是共享资源,所以,申请互斥锁和释放互斥锁的操作被设计成原子性操作。
  • 在纯互斥环境下,如果锁分配不够合理就容易导致其他线程的饥饿问题。
  • 在临界区中,线程可以被切换,但在线程被切换出去的时候,其是持有锁被切换的。在这期间没有谁能进入该互斥锁所锁定的临界区去访问临界资源。
  • 持有互斥锁的线程访问临界区的过程,对于其他线程来说是原子的。

2、使用场景

在这里插入图片描述

3、初始化

(1)函数

在这里插入图片描述

(2)概念

  • pthread_mutex_init函数将使用attr指定的属性初始化mutex引用的互斥锁。如果attr为NULL,则使用默认的互斥属性,其效果与传递默认互斥属性对象的地址的效果相同。
  • 只有互斥锁本身可用于执行同步。在调用pthread_mutex_lock函数、pthread_mutex_trylock函数、pthread_mutex_unlock函数和pthread_mutex_destroy函数时引用互斥锁副本的结果是未定义的。
  • 在默认互斥锁属性适用的情况下,宏PTHREAD_MUTEX_INITIALIZER可用于初始化静态分配的互斥锁。该效果等同于通过调用pthread_mutex_init函数,并将参数attr指定为NULL进行动态初始化,但不执行错误检查。
  • 尝试初始化已初始化的互斥锁的行为是未定义的。
  • 初始化成功后,互斥锁的状态将被初始化且为解锁状态。

4、销毁

(1)函数

在这里插入图片描述

(2)概念

  • pthread_mutex_destroy函数的作用为将mutex引用的互斥对象销毁。
  • 互斥对象实际上会变成未初始化。即pthread_mutex_destroy函数会将mutex引用的对象设置为无效值。
  • 被销毁的互斥对象可以使用pthread_mutex_init函数重新初始化。而在对象被销毁后,以其他方式引用该对象的结果是未定义的。
  • 销毁已解锁且初始化的互斥锁是安全的,而试图销毁锁定的互斥锁会导致未定义的行为。
  • 使用 PTHREAD_ MUTEX_ INITIALIZER 初始化的互斥锁不需要调用pthread_mutex_destroy函数销毁。

5、加锁

(1)函数

在这里插入图片描述

(2)概念

  • mutex引用的互斥对象可通过调用pthread_mutex_lock函数来锁定。如果该互斥锁已被锁定,则调用的线程会进行阻塞等待,直到互斥锁可用。此操作(申请锁)成功将返回处于锁定状态的互斥对象,调用线程作为其所有者。
  • 申请互斥锁成功后,尝试重新锁定互斥锁会导致死锁。如果一个线程试图解锁它没有锁定的互斥锁或处于解锁状态的互斥锁,则会导致未定义的行为,返回错误。
  • pthread_mutex_trylock函数等效于pthread_mutex_lock函数,但如果mutex引用的互斥对象当前被锁定(已被任何线程,包括当前线程申请成功),则调用会立即返回。

6、解锁

(1)函数

在这里插入图片描述

(2)概念

  • pthread_mutex_unlock函数释放mutex引用的互斥对象。而互斥锁的释放方式取决于互斥锁的类型属性。
  • 如果在调用pthread_mutex_unlock函数时,mutex引用的互斥对象上有线程被阻塞,则当互斥对象可用时,调度策略应确定哪个线程应获取该互斥对象。
  • 如果一个信号被传递给等待互斥锁的线程,那么在信号处理程序返回时,线程应继续等待之前等待的互斥锁,就像该线程没有被中断一样。

7、示例代码

pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
int count = 0;
class threadData
{
public:threadData(int number/*, pthread_mutex_t *lock*/)//:_lock(lock){_threadName = "thread-" + to_string(number);}string _threadName;//pthread_mutex_t *_lock;
};void *Routine(void *args)
{threadData *td = static_cast<threadData*>(args);while(true){pthread_mutex_lock(&lock);//pthread_mutex_lock(td->_lock);cout << td->_threadName << ", counting: " << count++ << endl;pthread_mutex_unlock(&lock);//pthread_mutex_unlock(td->_lock);if(count >= 6)break;sleep(1);}cout << td->_threadName << " quit" << endl;return nullptr;
}int main()
{vector<pthread_t> tids;vector<threadData*> tds;// pthread_mutex_t lock;// pthread_mutex_init(&lock, nullptr);for(int i = 0; i < 4; ++i){pthread_t tid;threadData *td = new threadData(i/*, &lock*/);tds.push_back(td);pthread_create(&tid, nullptr, Routine, td);tids.push_back(tid);}for(auto tid : tids){pthread_join(tid, nullptr);}cout << "main thread wait thread over" << endl;for(auto td : tds){delete td;}cout << "main thread delete thread over" << endl;//pthread_mutex_destroy(&lock);return 0;
}

8、运行结果

在这里插入图片描述

三、安全

1、线程安全

  • 如果线程是安全的,则多个线程并发执行同一段代码时,不会出现不同的结果。
  • 在没有锁保护的情况下,对全局变量或者静态变量进行操作,会出现线程安全问题。

2、重入

  • 重入为同一个函数被不同的执行流调用时,当前一个流程还没有执行完,就有其他的执行流再次进入。
  • 一个函数在重入的情况下,运行结果不会出现任何不同或者任何问题,则该函数为可重入函数。否则则为不可重入函数。

3、常见的线程不安全的情况

  • 不保护共享变量的函数。
  • 函数状态随着被调用,状态发生变化的函数。
  • 返回指向静态变量指针的函数。
  • 调用线程不安全函数的函数。

四、死锁

1、概念

  • 两个或两个以上的线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时系统处于死锁状态或系统产生了死锁。这些永远在互相等待的线程为死锁线程。
  • 在死锁情况下,所占用的资源或者需要它们进行某种合作的其它线程会相继陷入死锁,最终可能导致整个系统处于瘫痪状态。

2、产生的必要条件

  • 互斥条件:一个资源每次只能被一个执行流使用。这是产生死锁的前提。
  • 请求与保持条件:一个执行流因请求资源而阻塞时,对已获得的资源保持不放。这是产生死锁的原则。
  • 不剥夺条件:一个执行流已获得的资源,在末使用完之前,不能强行剥夺。这是产生死锁的原则。
  • 循环等待条件:若干执行流之间形成一种头尾相接的循环等待资源的关系。这是产生死锁的重要条件。

3、避免方法

  • 破坏四个必要条件。
  • 加锁的顺序一致。
  • 避免锁未释放的场景。
  • 资源一次性分配。

五、条件变量

1、背景概念

  • 同步:在保证数据安全的前提下,让线程能够按照某种特定的顺序访问临界资源,从而有效避免饥饿问题。
  • 竞态条件:因为时序问题导致程序异常。
  • 条件变量是一种线程同步的基本机制,用于线程之间发信号通知和等待。 它通常和互斥锁一起使用,以防止竞态条件和确保线程安全。即条件变量必须依赖于锁的使用。
  • 条件变量的优点是可以以原子方式阻塞线程,直到某个特定条件为真为止。

2、初始化与销毁

在这里插入图片描述

3、等待

(1)函数

在这里插入图片描述

(2)参数意义

  • 参数cond表示调用该函数的线程要在这个条件变量上等待。
  • 参数mutex表示当线程申请不到mutex时,在条件变量cond下等待。
  • 在等待时,如果不能申请到条件变量,则会释放锁mutex。

(3)互斥锁的作用

  • 条件等待是线程间同步的一种手段,如果只有一个线程,条件不满足,会一直等下去,所以必须要有一个线程通过某些操作改变共享变量,使原先不满足的条件变得满足,并且友好的通知等待在条件变量上的线程。
  • 条件不会无缘无故的突然变得满足,这必然会牵扯到共享数据的变化。所以,需要用互斥锁来保护,没有
    互斥锁就无法安全的获取和修改共享数据。

4、唤醒

(1)函数

在这里插入图片描述

(2)概念

  • pthread_cond_broadcast函数可解除当前阻塞在指定条件变量cond上的所有线程。
  • 如果在cond上有任何线程被阻塞,pthread_cond_signal函数可解除当前阻塞在指定条件变量cond上的至少一个线程。
  • 如果当前没有线程阻塞在条件变量cond上,则pthread_cond_broadcast和pthread_cond_signal函数将无效。

5、示例代码

pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int cnt = 0;void *Rountine(void *args)
{pthread_detach(pthread_self());uint64_t number = (uint64_t)args;cout << "thread " << number << " created" << endl;while(true){pthread_mutex_lock(&lock);//此处可加判断语句,不满足再在条件变量下等待pthread_cond_wait(&cond, &lock);cout << "thread " << number << " count, cnt = " << cnt++ << endl;pthread_mutex_unlock(&lock);}
}int main()
{for(uint64_t i = 0; i < 4; ++i){pthread_t tid;pthread_create(&tid, nullptr, Rountine, (void*)i);usleep(1000);}while(true){sleep(2);//pthread_cond_signal(&cond);//cout << "main thread open one cond's" << endl;pthread_cond_broadcast(&cond);cout << "main thread open all cond's" << endl;}return 0;
}

6、运行结果

在这里插入图片描述

  • 释放pthread_cond_signal以及下一行的注释,注释pthread_cond_broadcast以及下一行

在这里插入图片描述

六、生产者消费者模型

1、概念

  • 生产者消费者模式就是通过一个容器来解决生产者和消费者的强耦合问题。
  • 生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯。所以,生产者生产完数据之后不用等待消费者处理,而是直接扔给阻塞队列;消费者不找生产者要数据,而是直接从阻塞队列里取。
  • 阻塞队列相当于一个缓冲区,平衡生产者和消费者的处理能力和用来给生产者和消费者解耦。
  • 生产者和生产者之间是互斥关系、消费者和消费者之间是互斥关系、生产者和消费者之间是互斥且同步关系。
  • 模型包括两种角色,生产者和消费者。
  • 模型需要一个交易场所,即特定结构的内存空间。

2、示意图

在这里插入图片描述

3、优点

  • 解耦
  • 支持并发
  • 支持忙闲不均

4、环形队列

(1)示意图

在这里插入图片描述

(2)说明

  • 当环形队列满和空时,head和tail指向的是同一个位置。此时只能一方进行访问,当队列空时,生产者进行访问;当队列满时,消费者进行访问。
  • 消费者不能超过生产者,生产者不能超过消费者一个环形队列的长度

七、POSIX信号量

1、概念

  • 信号量是一个计数器,用于限制对共享资源的访问数量。
  • 当多个线程或进程需要访问共享资源时,它们会先尝试获取信号量。如果信号量的值大于0,则允许一个线程或进程获取信号量并访问共享资源,然后将信号量的值减1。否则则会等待。
  • 信号量把判断资源是否就绪放在了临界区之外,当申请信号量时,其实间接得在判断资源是否就绪。

2、初始化

(1)函数

在这里插入图片描述

(2)概念

  • sem_init函数的作用为在sem指向的地址初始化未命名的信号量。
  • value参数指定信号量的初始值。
  • pshared参数指示此信号量是在进程的线程之间共享,还是在进程之间共享。
  • 如果pshared的值为0,则信号量在进程的线程之间共享,并且应该位于所有线程可见的某个地址。如全局变量或在堆上动态分配的变量。
  • 如果pshared为非零,则信号量在进程之间共享,并且应该位于共享内存区域中。任何可以访问共享内存区域的进程都可以使用sem_post、sem_wait等对信号量进行操作。
  • 初始化已初始化的信号量的行为是未定义。

3、销毁

(1)函数

在这里插入图片描述

(2)概念

  • sem_destroy函数的作用为销毁sem指向的地址处的未命名信号量。
  • 只有由sem_init函数初始化的信号量才应该使用sem_destroy函数销毁。
  • 销毁当前有其他进程或线程被阻塞的信号量(即其在sem_wait中)会产生未定义的行为。
  • 使用已被销毁的信号量会产生未定义的结果,除非使用sem_init函数重新初始化该信号量。

4、等待

(1)函数

在这里插入图片描述

(2)概念

  • sem_wait函数递减(锁定)sem指向的信号量。
  • 如果信号量的值大于零,则递减继续进行,函数立即返回。
  • 如果信号量当前的值为零,则调用会阻塞,直到可以执行递减(即信号量值升至零以上),或者信号处理程序中断该调用。

5、发布

(1)函数

在这里插入图片描述

(2)概念

  • sem_post函数递增(解锁)sem指向的信号量。
  • 如果信号量的值因此变得大于零,则sem_wait调用中阻塞的另一个进程或线程将被唤醒并继续进行锁定信号量的操作。

八、单例模式

1、概念

  • 单例模式(Singleton Pattern)是一种设计模式。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
  • 这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。
  • 这个类提供了一种访问其唯一的对象的方式(全局访问点),可以直接访问,不需要实例化该类的对象。

2、实现方式

(1)饿汉模式

  • 饿汉模式是一种设计模式,它在类加载时就完成了实例化操作。因此,在类加载时比较慢,但在获取对象时速度比较快。
  • 它的优点是实现简单,线程安全。因为实例在类加载时就已经创建,所以不存在多线程下的同步问题。
  • 它的缺点是在类加载时就完成了实例化,如果这个类在程序运行过程中从未被使用到,那么就会造成资源的浪费。

(2)懒汉模式

  • 懒汉模式是一种在类加载时不进行实例化,只有在首次调用时才创建实例的设计模式。它的实现通常需要使用到同步机制来保证线程安全。
  • 它的优点是可以延迟实例化,只有在实际需要时才会创建对象。
  • 它的缺点是线程不安全,在多线程环境下,如果没有正确的同步机制,就可能会出现多个实例的情况。

九、其他常见的锁

  • 悲观锁:在每次取数据时,总是担心数据会被其他线程修改。所以会在取数据前先加锁(读锁,写锁,行锁等),当其他线程想要访问数据时,会被阻塞挂起。
  • 乐观锁:在每次取数据的时,总是乐观的认为数据不会被其他线程修改,因此不上锁。但是在更新数据前,会判断其他数据在更新前有没有对数据进行修改。主要采用两种方式,即版本号机制和CAS操作。
  • CAS操作:当需要更新数据时,判断当前内存值和之前取得的值是否相等。如果相等则用新值更新。若不相等则失败,失败则重试,一般是一个自旋的过程,即不断重试。
  • 读写锁:专门处理多读少写的情况,写时独占锁,读时共享锁,读锁的优先级比写锁高。写者之间是互斥竞争关系,写者与读者之间是互斥同步关系,读者之间是共享关系。

本文到这里就结束了,如有错误或者不清楚的地方欢迎评论或者私信
创作不易,如果觉得博主写得不错,请点赞、收藏加关注支持一下💕💕💕

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/396744.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SAM-Med2D 大模型学习笔记(续):训练自己数据集

1、前言、数据集介绍 SAM-Med2D大模型介绍参考上文&#xff1a;第三章&#xff1a;SAM-Med2D大模型复现-CSDN博客 本文将使用SAM-Med2D大模型训练自己的数据集 关于SAM-Med2D大模型官方demo数据集的介绍上文已经介绍过&#xff0c;这里简单回顾下 其中data_demo为数据集的目…

leetcode171. Excel 表列序号,进制转换

leetcode171. Excel 表列序号 给你一个字符串 columnTitle &#xff0c;表示 Excel 表格中的列名称。返回 该列名称对应的列序号 。 例如&#xff1a; A -> 1 B -> 2 C -> 3 … Z -> 26 AA -> 27 AB -> 28 … 示例 1: 输入: columnTitle “A” 输出: 1 示…

电商平台产品ID|CDN与预渲染|前端边缘计算

技术实现 都是通过ID拿到属性&#xff0c;进行预渲染html&#xff0c;通过 oss 分发出去 详情页这种基本都是通过 ssr 渲染出来&#xff0c;然后上缓存 CDN 分发到边缘节点来处理&#xff0c;具体逻辑可以参考 淘宝——EdgeRoutine边缘计算&#xff08;CDNServerless 边缘计算…

国内真正意义上的OpenAI,最强多模态大模型 MiniCPM-V 2.6 发布

最近这一两周看到不少互联网公司都已经开始秋招提前批了。不同以往的是&#xff0c;当前职场环境已不再是那个双向奔赴时代了。求职者在变多&#xff0c;HC 在变少&#xff0c;岗位要求还更高了。 最近&#xff0c;我们又陆续整理了很多大厂的面试题&#xff0c;帮助一些球友解…

二叉树的最大深度

二叉树的最大深度 思路&#xff1a; 法一&#xff1a;深搜 也就是递归 要想清楚边界条件 好久没写深搜了 回忆下怎么写。 突然就悟了&#xff1a; /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *rig…

2024年6月 青少年机器人技术等级考试理论综合试卷(二级)

202406 青少年等级考试机器人理论真题二级 第 1 题 如图&#xff0c;这是飞机起飞时的机翼示意图&#xff0c;下列说法正确的是&#xff1f;&#xff08; &#xff09; A&#xff1a;机翼上侧所受的气压为0 B&#xff1a;机翼受到向下的力的作用 C&#xff1a;机翼下侧所受…

基于sklearn的机器学习 — 支持向量机(SVM)

支持向量机&#xff08;SVM&#xff1a;support vector machine&#xff09;另一种功能强大、应用广泛的学习算法&#xff0c;可应用于分类、回归、密度估计、聚类等问题。SVM可以看作是感知器&#xff08;可被视为一种最简单形式的前馈神经网络&#xff0c;是一种二元线性分类…

C++ 特殊类设计

目录 0.前言 1.设计一个不能被拷贝的类 1.1C98实现 1.2C11实现 2.设计一个只能在堆上创建对象的类 3.设计一个只能在栈上创建对象的类 4.设计一个不能被继承的类 4.1C98实现 4.2C11实现 5.设计只能创建一个对象的类&#xff08;单例模式&#xff09; 5.1设计模式简介 5.2单例模…

Jupyter nbextensions安装与使用

这里写自定义目录标题 Jupyter nbextensions安装与使用安装7以下版本&#xff0c;安装插件包推荐使用的插件 Jupyter nbextensions安装与使用 目前&#xff0c;jupyter版本升级到了7以上版本&#xff0c;导致其界面非常难看&#xff0c;因此&#xff0c;为了重回之前的使用界面…

buuctf-crypto

前言 查找资料的时候,意外翻出之前刷的一些ctf题目,算是简单记录一下,当然因为常用typeo去写md文件,所以其中有很多当时记录的图片都失效了,可惜了 题目1:一眼就解密 ZmxhZ3tUSEVfRkxBR19PRl9USElTX1NUUklOR30 base64解密 flag:flag{THE_FLAG_OF_THIS_STRING} 题目2:MD5 …

全球化浪潮下的数据库革新:嘉里物流 TiDB 实践价值的设想

导读 本文来自 TiDB 社区武汉站——嘉里物流架构团队负责人肖飞老师的演讲《嘉里物流 & TiDB 在全球化业务场景中应用设想》。本次分享探讨了嘉里物流在全球化扩展中&#xff0c;将如何通过 TiDB 的强大功能应对海量数据挑战&#xff0c;优化技术架构&#xff0c;并提升决…

【Linux】详解自定义Shell管道 | 构建简易进程池

目录 续&#xff1a;通信 4 种情况 应用场景 1. 自定义 shell 管道 1. 包含头文件 2. 解析命令函数 详细步骤 3. 执行命令函数 4. 主函数 总结 2. 使用管道实现一个简易版本的进程池 代码结构 代码实现 channel.hpp tasks.hpp main.cc 子进程读取任务&#xff…

十九、虚拟机VMware Workstation(CentOSDebian)的安装

目录 &#x1f33b;&#x1f33b; 一、安装 VMware Workstation1.1 安装 VMware Workstation1.2 虚拟机上安装 CentOS1.3 虚拟机安装 Debian 二、配置Debian方便第三方工具远程连接2.1 配置debian2.2 安装远程SSH工具并连接 一、安装 VMware Workstation 官网下载 本地资源库…

你好! Git——企业级开发模型

企业级开发模型&#xff08;6&#xff09; 一、删除远程分支&#xff0c;git branch -a &#xff08;查看所有本地分支与远程分支&#xff09;还能看到已经删除的分支&#xff0c;怎么解决&#xff1f;二、企业级开发流程2.1 企业级开发流程2.2 系统开发环境 三、Git分支设计模…

RabbitMQ面试题汇总

RabbitMQ面试题 一、RabbitMQ基础1. 什么是RabbitMQ&#xff0c;它的基本架构是怎样的&#xff1f;2. RabbitMQ支持哪些协议&#xff1f;3. 说一下AMQP协议&#xff1f;4. 为什么要使用RabbitMQ&#xff1f;5. MQ的应用场景有哪些&#xff1f;6. 解耦、异步、削峰是什么&#x…

购物系统小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;商品分类管理&#xff0c;商品信息管理&#xff0c;特价商品管理&#xff0c;用户管理&#xff0c;留言板管理&#xff0c;订单管理&#xff0c;系统管理 微信端账号功能包括&#xff1a;系统首页&…

uni-app总结

1. <u-form-item label"报废人" ><u--input v-model"model.remark" border"bottom" placeholder"请输入"></u--input> </u-form-item> border"bottom" 报废日期 为了

后端Web开发之Maven

1.java项目构建工具maven介绍 Maven是apache旗下的一个开源项目。Apache软件基金会&#xff0c;成立于1999年7月&#xff0c;是目前世界上最大的最受欢迎的开源&#xff08;源代码开放&#xff09;软件基金会也是一一个专门为支持开源项目而生的非盈利性组织。 apache开源项目…

PDO在CANopen协议同步传输和异步传输

PDO&#xff08;过程数据对象&#xff09;在CANopen协议中有两种主要的传输方式&#xff1a;同步传输和异步传输。这两种方式决定了PDO数据的传输时机和条件。下面分别举例说明这两种传输方式&#xff1a; 1. 同步传输 (Synchronous Transmission) 概念&#xff1a; 在同步传输…

3GPP 4G 5G 主要协议

4G LTE的协议主要是36 series 5G NR的协议主要是38 series