当前位置: 首页 > news >正文

C#多线程

一、多线程实现方式
        1. 使⽤Thread类: System.Threading.Thread 类是C#中最基本的多线程编程⼯具。

        2. 使⽤ThreadPool: 线程池是⼀个管理和重⽤线程的机制,它可以在应⽤程序中创建和使 ⽤多个线程,⽽⽆需显式地管理线程的⽣命周期。你可以使⽤ ThreadPool.QueueUserWorkItem ⽅法将⼯作项添加到线程池中执⾏

        3. 使⽤Task类(推荐): System.Threading.Tasks.Task 类是.NET Framework 4.0引⼊的并⾏编程⼯具,它提供了更⾼级别的抽象,简化了多线程编程。使⽤ Task.Run ⽅ 法可以很⽅便地创建并启动新线程

二、.C# 5 引⼊的 async/await 关键字是⽤来做什么的?它与传统 的多线程编程有什么不同?         async/await 是C# 5 中引⼊的⼀种异步编程模式,⽤于简化异步操作的编写和管理。它可 以帮助开发者编写更清晰、更易读的异步代码,同时避免了传统多线程编程中可能出现的⼀些 问题。 async/await 并不是创建新线程的⽅式,⽽是⼀种对异步操作的任务管理机制。 异步编程和多线程的区别:

        1. 可读性: async/await 的代码结构更加清晰易读。传统的多线程编程可能会涉及显 式地创建、启动和管理线程,⽽ async/await 让你可以将异步操作以类似于同步代 码的⽅式进⾏编写,不需要关⼼底层线程的管理。

        2. 阻塞和⾮阻塞: 使⽤ async/await 可以避免阻塞主线程。在传统的多线程编程中, 如果主线程需要等待⼀个操作完成,可能需要使⽤阻塞⽅式等待。⽽ async/await 允许主线程在等待异步操作的同时保持⾮阻塞状态,提⾼了程序的响应性。

        3. 上下⽂切换: 传统的多线程编程可能涉及线程切换的开销,⽽ async/await 不会直 接引⼊线程切换。它使⽤了异步任务的调度器来管理任务的执⾏,这可能会在需要的时候 重⽤线程,减少上下⽂切换的成本。

        4. 异常处理: async/await 更好地处理了异常。异步操作中的异常会在 await 表 达式中正确地捕获,使得异常处理更加简单和可靠。

         5. 资源管理: 传统多线程编程中需要⼿动管理资源的释放,⽽ async/await 通常能够 更好地管理资源的⽣命周期。 总之, async/await 是⼀种更现代、更简洁的异步编程⽅式,相较于传统的多线程编程,它 能够提供更好的可读性、更好的性能和更少的错误。

三、线程安全

常见的线程安全问题

        竞争条件(Race Condition):当多个线程并发访问共享资源时,可能会导致竞争条件。例如,当多个线程通过递增操作改变一个共享变量的值时,可能会导致值的不确定性。

        死锁(Deadlock):当多个线程相互等待彼此释放某些资源时,可能会导致死锁。在死锁状态下,程序停止响应,无法正常运行。

        内存泄漏(Memory Leak):内存泄漏是指程序运行时不断分配内存,但不及时释放,导致内存使用过多。这可能会影响程序的性能和可靠性。

        线程干扰(Thread Interference):线程干扰是指在线程间共享数据时,未正确同步数据所导致的问题。这可能导致数据丢失或不一致的情况。

解决方法
以下是一些解决线程安全问题的方法:

        互斥锁:互斥锁是一种常用的线程同步机制,它能够保护共享资源,确保多个线程访问资源时不会产生冲突。在C#中,可使用lock关键字来实现互斥锁。

        原子操作:原子操作是指在CPU执行某个操作时,该操作不会中断或被其他线程所干扰。通过使用原子操作,我们可以避免竞争条件的问题。

        并发集合(Concurrent Collections):并发集合是一种特殊的集合类型,它是线程安全的。在C#中,ConcurrentQueue、ConcurrentStack和ConcurrentDictionary等类就是并发集合。

        线程安全的类型(Thread-Safe Types):线程安全的类型是指可以安全地访问和修改数据的类型。在C#中,有一些类型(如StringBuilder、DateTime和String等)是线程安全的。
四、锁

        1、lock关键字

        如果说c#中的锁,那么首当其冲的就是lock关键字了。给lock关键字指定一个引用对象,然后上锁,保证同一时间只能有一个线程在锁里。这应该是最我们最常用的场景了。注意:我们说的是一把锁里同时只能有一个线程,至于这把锁用在了几个地方,那就不确定了。比如:object lockobj=new object(),这把锁可以锁一个代码块,也可以锁多个代码块,但无论锁多少个代码块,同一时间只能有一个线程打开这把锁进去,所以会有人建议,不要用lock(typeof(Program))或lock(this)这种锁,因为这把锁是所有人能看到的,别人可以用这把锁锁住自己的代码,这样就会出现一把锁锁住多个代码块的情况了,但现实使用中,一般没人会这么干,所以即使我们在阅读开源工程的源码时也能常常见到lock(typeof(Program))这种写法,不过还是建议用私有字段做锁,下面给出锁的几中应用场景:

class Program
{private readonly object lockObj = new object();private object obj = null;public void TryInit(){if (obj == null){lock (lockObj){if (obj == null){obj = new object();}}}}
}

自动编号

class DemoService
{private static int id;private static readonly object lockObj = new object();public void Action(){//do somethingint newid;lock (lockObj){newid = id + 1;id = newid;}//use newid...}}

 最后: 需要说明的是,lock关键字只不过是Monitor的语法糖,也就是说下面的代码:

lock (typeof(Program))
{int i = 0;//do something
}

被编译成IL后就变成了:

try
{Monitor.Enter(typeof(Program));int i = 0;//do something}finally{Monitor.Exit(typeof(Program));}
注意:lock关键字不能跨线程使用,因为它是针对线程上的锁。下面的代码是不被允许的(异步代码可能在await前后切换线程):想实现异步锁,参照后面的:《SemaphoreSlim》

        2.Monitor

        上面说了lock关键字是Monitor的语法糖,那么肯定Monitor功能是lock的超集,所以这里讲讲Monitor除了lock的功能外还有什么:

        Monitor.Wait(lockObj):让自己休眠并让出锁给其他线程用(其实就是发生了阻塞),直到其他在锁内的线程发出脉冲(Pulse/PulseAll)后才可从休眠中醒来开始竞争锁。Monitor.Wait(lockObj,2000)则可以指定最大的休眠时间,如果时间到还没有被唤醒那么就自己醒。注意: Monitor.Wait有返回值,当自己醒的时候返回false,当其他线程唤醒的时候返回true,这主要是用来防止线程锁死,返回值可以用来判断是否向后执行或者是重新发起Monitor.Wait(lockObj)
Monitor.Pulse或Monitor.PulseAll:唤醒由于Monitor.Wait休眠的线程,让他们醒来参与竞争锁。不同的是:Pulse只能唤醒一个,PulseAll是全部唤醒。这里顺便提一下:在多生产者、多消费者的情况下,我们更希望去唤醒消费者或者是生产者,而不是谁都唤醒,在java中我们可以使用lock的condition来解决这个问题,在c#中我们可以使用下面介绍的ManaualResetEvent或AutoResetEvent

 

System.Object obj = (System.Object)x;
System.Threading.Monitor.Enter(obj);
try
{DoSomething();
}
finally
{System.Threading.Monitor.Exit(obj);
}

3、ReaderWriteLock[Slim]

        我们知道,Monitor实现的是在读写两种情况的临界区中只可以让一个线程访问,那么如果业务中存在”读取密集型“操作,就好比数据库一样,读取的操作永远比写入的操作多。针对这种情况,我们使用Monitor的话很吃亏,不过没关系,ReadWriterLock[Slim]就很牛X,因为实现了”写入串行“,”读取并行“。
ReaderWriteLock[Slim]中主要用3组方法:

<1> AcquireWriterLock[TryEnterReadLock]: 获取写入锁。
ReleaseWriterLock:释放写入锁。

<2> AcquireReaderLock: 获取读锁。
ReleaseReaderLock:释放读锁。

<3> UpgradeToWriterLock:将读锁转为写锁。
DowngradeFromWriterLock:将写锁还原为读锁。
 

 并行读

using System;
using System.Threading;class Program
{//static ReaderWriterLock readerWriterLock = new ReaderWriterLock();static ReaderWriterLockSlim readerWriterLock = new ReaderWriterLockSlim();public static void Main(string[] args){var thread = new Thread(() =>{Console.WriteLine("thread1 start...");//readerWriterLock.AcquireReaderLock(3000);readerWriterLock.TryEnterReadLock(3000);int index = 0;while (true){index++;Console.WriteLine("du...");Thread.Sleep(1000);if (index > 6) break;}//readerWriterLock.ReleaseReaderLock();readerWriterLock.ExitReadLock();});thread.Start();var thread2 = new Thread(() =>{Console.WriteLine("thread2 start...");//readerWriterLock.AcquireReaderLock(3000);readerWriterLock.TryEnterReadLock(3000);int index = 0;while (true){index++;Console.WriteLine("读...");Thread.Sleep(1000);if (index > 6) break;}//readerWriterLock.ReleaseReaderLock();readerWriterLock.ExitReadLock();});thread2.Start();Console.ReadLine();}
}

串行写

using System;
using System.Threading;class Program
{//static ReaderWriterLock readerWriterLock = new ReaderWriterLock();static ReaderWriterLockSlim readerWriterLock = new ReaderWriterLockSlim();public static void Main(string[] args){var thread = new Thread(() =>{Console.WriteLine("thread1 start...");//readerWriterLock.AcquireWriterLock(1000);readerWriterLock.TryEnterWriteLock(1000);Console.WriteLine("写...");Thread.Sleep(5000);Console.WriteLine("写完了...");//readerWriterLock.ReleaseReaderLock();readerWriterLock.ExitWriteLock();});thread.Start();var thread2 = new Thread(() =>{Console.WriteLine("thread2 start...");try{//readerWriterLock.AcquireReaderLock(2000);readerWriterLock.TryEnterReadLock(2000);Console.WriteLine("du...");//readerWriterLock.ReleaseReaderLock();readerWriterLock.ExitReadLock();Console.WriteLine("du wan...");}catch (Exception ex){Console.WriteLine(ex.Message);}});Thread.Sleep(100);thread2.Start();Console.ReadLine();}
}

从上面的试验可以看出,“读“和“写”锁是不能并行的,他们之间相互竞争,同一时间,里面可以有一批“读”锁或一个“写”锁 ,其他的则不允许。

另外,我们在程序中应该尽量使用ReaderWriterLockSlim,而不是ReaderWriterLock,关于这点,可以看官方文档描述:

4.mutex

        Mutex的实现是调用操作系统层的功能,所以Mutex的性能要略慢一些,而它所能锁住的范围更大(它能跨进程上锁),但是它的功能也就相当于lock关键字(因为没有类似Monitor.Wait和Monitor.Pulse的方法)。
Mutex分为命名的Mutex和未命名的Mutex,命名的Mutex可用来跨进程加锁,未命名的相当于lock。
所以说:在一个进程中使用它的场景真的不多。它的比较常用场景如:限制一个程序在一个计算机上只能允许运行一次:

class Program
{private static Mutex mutex = null;static void Main(){bool firstInstance;mutex = new Mutex(true, @"Global\MutexSampleApp", out firstInstance);try{if (!firstInstance){Console.WriteLine("已有实例运行,输入回车退出……");Console.ReadLine();return;}else{Console.WriteLine("我们是第一个实例!");for (int i = 60; i > 0; --i){Console.WriteLine(i);Thread.Sleep(1000);}}}finally{if (firstInstance){mutex.ReleaseMutex();}mutex.Close();mutex = null;}}
}

需要注意的地方:

new Mutex(true, @"Global\MutexSampleApp", out firstInstance)代码不会阻塞当前线程(即使第一个参数为true),在多进程协作的时候最后一个参数firstInstance很重要,要善于运用。
mutex.WaitOne(30*1000)代码,当前进程正在等待获取锁的时候,已占用了这个命名锁的进程意外退出了,此时当前线程并不会直接获得锁然后向后执行,而是抛出异常AbandonedMutexException,所以在等待获取锁的时候要记得加上try catch。可以参照下面的代码:
 

class Program
{private static Mutex mutex = null;static void Main(){mutex = new Mutex(false, @"Global\MutexSampleApp");while (true){try{Console.WriteLine("start wating...");mutex.WaitOne(20 * 1000);Console.WriteLine("enter success");Thread.Sleep(20 * 1000);break;}catch (AbandonedMutexException ex){Console.WriteLine(ex.Message);continue;}}//do somethingmutex.ReleaseMutex();Console.WriteLine("Released");Console.WriteLine("ok");Console.ReadKey();}
}

5、并发集合

        C#中的并发集合包括ConcurrentQueue、ConcurrentStack、ConcurrentBag、ConcurrentDictionary和BlockingCollection等。这些集合不仅提供了线程安全的访问,而且还具有高效的并发性能。

ConcurrentQueue是一个线程安全的队列,支持并发添加和删除元素。ConcurrentStack类似于ConcurrentQueue,不同之处在于它是一个栈而不是队列。ConcurrentBag则类似于一个集合,可以并发添加和删除元素,但不保证元素的顺序。ConcurrentDictionary是一个线程安全的字典,支持并发添加、删除和更新键值对。

另外一个比较有用的并发集合是BlockingCollection,它是一个基于生产者消费者模式的并发集合。它提供了一种方便的方式来在多个线程之间传递数据。当集合为空时,从BlockingCollection中获取数据的线程将被阻塞,直到有新数据添加到集合中。当集合已满时,向BlockingCollection中添加数据的线程将被阻塞,直到有足够的空间可用。

使用并发集合时,需要注意一些细节。例如,虽然并发集合是线程安全的,但是对于某些操作,如ConcurrentDictionary中的GetOrAdd方法,需要使用原子操作来确保线程安全。另外,由于并发集合具有高效的并发性能,因此在单线程环境下使用它们可能会导致性能下降。

总之,在多线程编程中,C#中的并发集合是一种非常有用的工具,可以帮助我们更轻松地实现线程安全的数据共享和修改。对于需要在多个线程之间共享数据的应用程序,使用并发集合可以极大地简化编程工作,并提高应用程序的性能和可靠性。

6. 悲观锁:

        所谓悲观锁,就是在进行操作时针对记录加上排他锁,这样其他事务如果想操作该记录,需要等待锁的释放。

悲观锁在处理并发量和频繁访问时,等待时间比较长,冲突概率高,并发性能不好。

7. 乐观锁

        乐观锁,是在提交对记录的更改时才将对象锁住,提交前需要检查数据的完整性。

相关文章:

C#多线程

一、多线程实现方式 1. 使⽤Thread类&#xff1a; System.Threading.Thread 类是C#中最基本的多线程编程⼯具。 2. 使⽤ThreadPool&#xff1a; 线程池是⼀个管理和重⽤线程的机制&#xff0c;它可以在应⽤程序中创建和使 ⽤多个线程&#xff0c;⽽⽆需显式地管理线程的…...

Unity 编辑器常用方法

unity编辑器开发 脚本注解1. RuntimeInitializeOnLoadMethod2. ColorUsage3. Header4. SerializeField5. HideInInspector6. Space7. Range8. Multiline9.[RequireComponent(typeof())]10.HelpURL 右键菜单注解1. CreateAssetMenu - 针对ScriptableObject 菜单栏注解1. MenuIt…...

21 mysql ref 查询

前言 这里主要是 探究一下 explain $sql 中各个 type 诸如 const, ref, range, index, all 的查询的影响, 以及一个初步的效率的判断 这里会调试源码来看一下 各个类型的查询 需要 lookUp 的记录 以及 相关的差异 此系列文章建议从 mysql const 查询 开始看 测试表结构…...

启山智软/一款包含主流商城类型的一款电商中台系统100%开源

文章目录 介绍一、Smart Shop JAVA 微服务电商中台优势二、电商中台包含那些主流商城模式1.S2B2C供应链商城2.B2B2C多商户商城3.B2C单商户商城4.O2O外卖配送商城5.社区团购商城 6.演示地址总结 介绍 想要了解代码规范&#xff0c;学习商城解决方案&#xff0c;点击下方官网链接…...

【C语言】指针的进阶(四)—— 企业笔试题解析

笔试题1&#xff1a; int main() {int a[5] { 1, 2, 3, 4, 5 };int* ptr (int*)(&a 1);printf("%d,%d", *(a 1), *(ptr - 1));return 0; } 【答案】在x86环境下运行 【解析】 &a是取出整个数组的地址&#xff0c;&a就表示整个数组&#xff0c;因此…...

博弈论——连续产量古诺模型

连续产量古诺模型 连续产量古诺模型是博弈论中非常经典的模型&#xff0c;以两厂商连续产量古诺博弈为例&#xff1a; 1、模型建立 Player&#xff1a;两个供应相同产品的厂商 产量&#xff1a;厂商1的产量为q1&#xff0c;厂商2的产量为q2&#xff0c;市场总供给为Qq1q2。…...

ROS2 驱动思岚G4雷达(ydlidar)- Rviz显示

记录G4雷达的配置 系统环境为&#xff1a;Ubuntu22.04 配置步骤 1、安装雷达SDK 2、构建 G4 雷达 ROS2 项目工程文件 3、使用Rviz可视化界面显示 1、安装雷达SDK 1.1 安装CMake YDLidar SDK需要CMake 2.8.2作为依赖项 Ubuntu 18.04或者Ubuntu 22.04 sudo apt install cmak…...

Spring Cloud Alibaba Sentinel流量防卫兵

文章目录 Spring Cloud Alibaba Sentinel流量防卫兵1. 分布式遇到的问题2.解决的方法 Sentinel: 分布式系统的流量防卫兵1. 简介和特折 Sentinel流量防卫兵的搭建1.引入依赖2.添加配置类3.运行类上添加SentinelResource&#xff0c;并配置blockHandler和fallback4. linux中放入…...

1.简单工厂模式

UML类图 代码 main.cpp #include <iostream> #include "OperationFactory.h" using namespace std;int main(void) {float num1;float num2;char operate;cin >> num1 >> num2 >> operate;Operation* oper OperationFactory::createOpera…...

GitHub Copilot Chat

9月21日&#xff0c;GitHub在官网宣布&#xff0c;所有个人开发者可以使用GitHub Copilot Chat。用户通过文本问答方式就能生成、检查、分析各种代码。 据悉&#xff0c;GitHub Copilot Chat是基于OpenAI的GPT-4模型打造而成&#xff0c;整体使用方法与ChatGPT类似。例如&…...

利用 QT 完成一个人脸识别系统,完成登录操作

1.配置文件 # Project created by QtCreator 2023-09-22T10:34:23 # #-------------------------------------------------QT core guigreaterThan(QT_MAJOR_VERSION, 4): QT widgetsTARGET project TEMPLATE appSOURCES main.cpp\widget.cppHEADERS widget.hFOR…...

MATLAB APP纯小白入门 两数相加

万事开头难&#xff0c;最怕第一次。使用matlab APP 实现两数求和&#xff0c;如下图所示&#xff0c;c a b&#xff0c;输入数字后&#xff0c;按 “” 就计算。 步骤 拖拽三个 Edit Field(Numeric) 过来&#xff0c;并且双击名字分别改为 a,b,c。注意修改名字后右边会有点变…...

ubuntu右上角的网络连接图标消失解决办法

ubuntu更新了几个文件后&#xff0c;我的ubuntu系统右上角的网络连接图标就消失了&#xff0c;然后怎么也找不到了&#xff0c;怎么办呢&#xff1f; 1、按快捷键ctrlaltt打开终端 2、按以下顺序输入如下的命令行 sudo service network-manager stop sudo rm /var/lib/Netw…...

conda创建虚拟环境安装aix360

目录 创建虚拟环境查看已有虚拟环境进入所创建的虚拟环境查看已安装的程序查看已安装的python模块配置镜像pipconda 安装aix360将环境添加到jupyter删除虚拟环境 创建虚拟环境 conda create -n aix360 python3.9查看已有虚拟环境 conda env list进入所创建的虚拟环境 activa…...

CentOS安装mariadb

1、 安装 [rootlocalhost ~]# yum install mariadb mariadb-server2、 启动并自启 [rootecs-3f21 ~]# systemctl enable mariadb –now3、 查看启动状态 [rootecs-3f21 ~]# systemctl status mariadb4、 初始化mariadb并设置root密码 [rootecs-3f21 ~]# mysql_secure_inst…...

FPGA——基础知识合集

文章目录 前言1、简述触发器与锁存器的区别2、简述 if-else 语句和 case 语句的区别3、相对 ARM、DSP 等处理器&#xff0c;谈谈 FPGA 具有哪些优势4、简述 Verilog 语句中阻塞赋值与非阻塞赋值的含义与区别&#xff0c;以及各自的适用的场景5、什么是同步电路&#xff0c;什么…...

【pytest】 标记冒烟用例 @pytest.mark.smoke

1. 使用 pytest.mark.smoke 标记用例 import pytest class Test_Smoke:def test_01(self):assert 112pytest.mark.smokedef test_02(self):assert 121pytest.mark.smokedef test_03(self):assert 1 2 3 2.配置文件pytest.ini [pytest] markers smoke 3. 运行指定标签 运…...

数据结构入门-14-排序

一、选择排序 1.1 选择排序思想 先把最小的元素拿出来 剩下的&#xff0c;再把最小的拿出来 剩下的&#xff0c;再把最小的拿出来 但是这样 空间复杂度是O(n) 优化一下&#xff0c;希望原地排序 1.1.2 选择原地排序 索引i指向0的位置 索引j指向i1的元素 j 后面的元素遍历&…...

Gin学习记录4——Controller和中间件

一. Controller 用不同的Controller可以实现业务的分类&#xff0c;不同类型的请求可以共用同一套中间件 1.1 单文件Controller 几乎等同于函数封装&#xff0c;直接将ctrl的代码写入到一个文件里然后调用&#xff1a; package adminimport ("net/http""git…...

FL Studio21.2中文版数字音乐制作软件

现在的FL也可以像splice一样啦&#xff0c;需要什么样的声音只需在fl里搜索&#xff0c;就会自动展示给你! FL Studio 简称FL&#xff0c;全称&#xff1a;Fruity Loops Studio&#xff0c;国人习惯叫它"水果"。软件现有版本是 FL Studio 21&#xff0c;已全面升级支…...

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…...

idea大量爆红问题解决

问题描述 在学习和工作中&#xff0c;idea是程序员不可缺少的一个工具&#xff0c;但是突然在有些时候就会出现大量爆红的问题&#xff0c;发现无法跳转&#xff0c;无论是关机重启或者是替换root都无法解决 就是如上所展示的问题&#xff0c;但是程序依然可以启动。 问题解决…...

云原生核心技术 (7/12): K8s 核心概念白话解读(上):Pod 和 Deployment 究竟是什么?

大家好&#xff0c;欢迎来到《云原生核心技术》系列的第七篇&#xff01; 在上一篇&#xff0c;我们成功地使用 Minikube 或 kind 在自己的电脑上搭建起了一个迷你但功能完备的 Kubernetes 集群。现在&#xff0c;我们就像一个拥有了一块崭新数字土地的农场主&#xff0c;是时…...

DAY 47

三、通道注意力 3.1 通道注意力的定义 # 新增&#xff1a;通道注意力模块&#xff08;SE模块&#xff09; class ChannelAttention(nn.Module):"""通道注意力模块(Squeeze-and-Excitation)"""def __init__(self, in_channels, reduction_rat…...

HBuilderX安装(uni-app和小程序开发)

下载HBuilderX 访问官方网站&#xff1a;https://www.dcloud.io/hbuilderx.html 根据您的操作系统选择合适版本&#xff1a; Windows版&#xff08;推荐下载标准版&#xff09; Windows系统安装步骤 运行安装程序&#xff1a; 双击下载的.exe安装文件 如果出现安全提示&…...

Typeerror: cannot read properties of undefined (reading ‘XXX‘)

最近需要在离线机器上运行软件&#xff0c;所以得把软件用docker打包起来&#xff0c;大部分功能都没问题&#xff0c;出了一个奇怪的事情。同样的代码&#xff0c;在本机上用vscode可以运行起来&#xff0c;但是打包之后在docker里出现了问题。使用的是dialog组件&#xff0c;…...

【笔记】WSL 中 Rust 安装与测试完整记录

#工作记录 WSL 中 Rust 安装与测试完整记录 1. 运行环境 系统&#xff1a;Ubuntu 24.04 LTS (WSL2)架构&#xff1a;x86_64 (GNU/Linux)Rust 版本&#xff1a;rustc 1.87.0 (2025-05-09)Cargo 版本&#xff1a;cargo 1.87.0 (2025-05-06) 2. 安装 Rust 2.1 使用 Rust 官方安…...

群晖NAS如何在虚拟机创建飞牛NAS

套件中心下载安装Virtual Machine Manager 创建虚拟机 配置虚拟机 飞牛官网下载 https://iso.liveupdate.fnnas.com/x86_64/trim/fnos-0.9.2-863.iso 群晖NAS如何在虚拟机创建飞牛NAS - 个人信息分享...

【p2p、分布式,区块链笔记 MESH】Bluetooth蓝牙通信 BLE Mesh协议的拓扑结构 定向转发机制

目录 节点的功能承载层&#xff08;GATT/Adv&#xff09;局限性&#xff1a; 拓扑关系定向转发机制定向转发意义 CG 节点的功能 节点的功能由节点支持的特性和功能决定。所有节点都能够发送和接收网格消息。节点还可以选择支持一个或多个附加功能&#xff0c;如 Configuration …...

高抗扰度汽车光耦合器的特性

晶台光电推出的125℃光耦合器系列产品&#xff08;包括KL357NU、KL3H7U和KL817U&#xff09;&#xff0c;专为高温环境下的汽车应用设计&#xff0c;具备以下核心优势和技术特点&#xff1a; 一、技术特性分析 高温稳定性 采用先进的LED技术和优化的IC设计&#xff0c;确保在…...