【Code】《代码整洁之道》笔记-Chapter13-并发编程

国央企创新负责人如何通过平台提升技术需求挖掘与资源配置的精准性?.docx 国央企创新负责人如何通过平台提升技术需求挖掘与资源配置的精准性? 立即下载

第13章 并发编程

“对象是过程的抽象。线程是调度的抽象。”

编写整洁的并发程序很难——非常难,而编写在单线程中执行的代码却简单得多。编写表面上看似不错、深入进去却支离破碎的多线程代码也简单,但是系统一旦遭受压力,这种代码就扛不住了。

本章将讨论并发编程的需求及其困难之处,并给出一些应对这些难点、编写整洁的并发代码的建议。最后,我们将讨论与测试并发代码有关的问题。

整洁的并发编程是个复杂话题,值得用一整本书来讨论。本书只做概览,并在附录A中提供更详细的指引。如果你只是对并发好奇,阅读本章就足够了。如果你需要更深入地理解并发,就应读完整个附录A。

13.1 为什么要并发

并发是一种解耦策略。它帮助我们把做什么(目的)和何时做(时机)分解开。在单线程应用中,目的与时机紧密耦合,很多时候只要查看栈追踪即可断定应用程序的状态。调试这种系统的程序员可以设定断点或者断点序列,通过查看到达哪个断点来了解系统状态。

解耦目的与时机能明显地改进应用程序的吞吐量和结构。从结构的角度来看,应用程序看起来更像是许多台协同工作的计算机,而不是一个大循环。系统因此会更被易于理解,给出了许多切分关注面的有力手段。

例如,Web应用的Servlet标准模式。这类系统运行于Web或EJB容器的保护伞之下,Web或EJB为你部分地处理并发问题。当有Web请求时,servlet就会异步执行。servlet程序员无须管理所有的请求。原则上,每次servlet是在自己的小世界中执行,与其他servlet的执行是分离的。

当然,如果只是那么简单,也就没必要写这一章了。实际上,Web容器提供的解耦手段离完美还差得远。servlet程序员得非常警惕、非常小心地保证并发程序不出错。同样,servlet模式的结构性好处还是很明显的。

但结构并非采用并发的唯一动机。有些系统对响应时间和吞吐量有要求,需要手工编写并发解决方案。例如,考虑一个单线程信息聚合程序,它从许多Web站点获取信息,再合并写入日志中。因为该系统是单线程的,它会逐个访问Web站点,在开始下一个访问之前等待当前站点访问完毕。每天的执行时间必须少于24小时。然而,随着要访问的站点越来越多,采集所有数据花费的时间也越来越多,最终超过了24小时的限制。单线程程序许多时间花在等待Web套接字I/O结束上面。通过采用同时访问多个站点的多线程算法,就能改进性能。

或者,考虑某个每次花费1秒处理一个用户请求的系统。该系统在用户量较少的时候响应及时,但随着用户数增加,系统的响应时间也增加了。没人想排在150个人后面!通过并发处理多个用户请求,就能改善系统响应时间。

再或者,考虑某个解释大量数据集,但只在处理完全部数据后给出一个完整解决方案的系统。或许可以在独立的计算机上处理每个数据集,那样的话许多数据集就能并行地得到处理。

迷思与误解

看来有足够的理由采用并发方案。然而,如前文所述,并发编程很难。如果你不那么细心,就会搞出不堪入目的东西来。看看以下常见的迷思和误解:

(1)并发总能改进性能。并发有时能改进性能,但只在多个线程或处理器之间能分享大量等待时间的时候管用。事情没那么简单。

(2)编写并发程序无须修改设计。事实上,并发算法的设计有可能与单线程系统的设计极不相同。目的与时机的解耦往往会对系统结构产生巨大影响。

(3)在采用Web或EJB容器的时候,理解并发问题并不重要。实际上,你最好了解容器在做什么,了解如何应对本章后面将提到的并发更新、死锁等问题。

下面是一些有关编写并发软件的中肯说法:

  • 并发会在性能和编写额外代码上增加一些开销;

  • 正确的并发是复杂的,即便对于简单的问题也是如此;

  • 并发缺陷并非总能重现,所以常被看作偶发事件而忽略,未被当作真的缺陷看待;

  • 并发常常需要对设计策略做根本性修改。

13.2 挑战

并发编程为何如此之难?来看看下面这个小型类:

public class X {
  private int lastIdUsed;

  public int getNextId() {
    return ++lastIdUsed;
  }
}

例如,创建X的一个实体,将lastIdUsed设置为42,在两个线程中共享这个实体。假设这两个线程都调用getNextId()方法,结果可能有以下3种输出:

  • 线程一得到值43,线程二得到值44,lastIdUsed为44;

  • 线程一得到值44,线程二得到值43,lastIdUsed为44;

  • 线程一得到值43,线程二得到值43,lastIdUsed为43。

第三种结果令人惊异,当两个线程相互影响时就会出现这种情况。这是因为线程在执行那行Java代码时有许多可能的路径,有些路径会产生错误的结果。有多少种不同路径呢?要真正回答这个问题,需要理解Just-In-Time编译器如何对待生成的字节码,还要理解Java内存模型认为什么东西具有原子性。

简单回答一下,就生成的字节码而言,对于在getNextId方法中执行的那两个线程,有12870种不同的可能执行路径。如果lastIdUsed的类型从int变为long,则可能路径的数量将增至2704156种。当然,多数路径都能得到正确结果,问题是其中一些不能得到正确结果。

13.3 并发防御原则

下面给出一系列防御并发代码问题的原则和技巧。

13.3.1 单一权责原则

单一权责原则(SRP)认为,方法/类/组件应当只有一个修改的理由。并发设计足够复杂,以至于这种复杂足以成为其需要修改的理由,所以确实应该从其他代码中分离出来。不幸的是,并发实现细节常常直接嵌入其他生产代码中。下面是要考虑的一些问题:

  • 并发相关代码有自己的开发、修改和调优生命周期;

  • 并发相关代码有自己要应对的挑战,它和非并发相关代码不同,而且往往更为困难;

  • 即便没有增加周边应用程序的负担,写得不好的并发代码可能的出错方式数量也已经足具挑战性。

建议:分离并发相关代码与其他代码。

13.3.2 推论:限制数据作用域

如我们所见,两个线程修改共享对象的同一字段时,可能会互相干扰,导致未预期的行为。解决方案之一是采用关键字synchronized在代码中保护一块使用共享对象的临界区(critical section)。限制临界区的数量很重要。更新共享数据的地方越多,就越可能:

  • 你会忘记保护一个或多个临界区——破坏了修改共享数据的代码;

  • 得多花力气保证一切都受到有效防护(违反了DRY原则);

  • 很难找到错误源,也很难判断错误源。

建议:谨记数据封装;严格限制对可能被共享的数据的访问。

13.3.3 推论:使用数据副本

避免共享数据的好方法之一是,从开始就避免共享数据。在某些情形下,有可能复制对象并以只读方式对待。在另外的情况下,有可能复制对象,从多个线程收集所有副本的结果,并在单个线程中合并这些结果。

如果有避免共享数据的简易手段,结果代码就会大大减小导致错误的可能。你可能会关心创建额外对象的成本,值得试验一下看看那是否真是个问题。然而,假使使用对象副本能避免代码同步执行,则因避免了锁定而省下的价值有可能补偿得上额外的创建成本和垃圾收集开销。

13.3.4 推论:线程应尽可能地独立

让每个线程在自己的世界中存在,而不与其他线程共享数据。每个线程处理一个客户端请求,从不共享的源头接纳所有请求数据,存储为本地变量。这样一来,每个线程都像是世界中的唯一线程,没有同步需要。

例如,HttpServlet的子类接收所有以参数形式传递给doGet方法和doPost方法的信息。每个Servlet都像拥有独立虚拟机一般运行。只要Servlet中的代码只使用本地变量,Servlet就不会导致同步问题。当然,多数使用Servlet的应用程序最终都还是会用到类似数据库连接之类的共享资源。

建议:尝试将数据分解为可被独立线程(可能在不同处理器上)操作的独立子集。

13.4 了解Java库

与之前的版本相比,Java 5提供了许多并发开发方面的改进。在用Java 5编写线程代码时,要注意以下几点:

- 使用类库提供的线程安全群集;

- 使用executor框架执行无关任务;

- 尽可能使用非锁定解决方案;

- 有几个类并不是线程安全的。

线程安全群集

当Java还年轻时,Doug Lea编写了《Java并发编程》(Concurrent Programming in Java)教程,同时开发了几个线程安全群集,这些代码后来成为JDK中java.util.concurrent包的一部分。该代码包中的群集对于多线程解决方案是安全的,执行良好。实际上,在几乎所有情况下,ConcurrentHashMap实现都比HashMap表现得好,它还支持同步并发读写,也拥有支持非线程安全的合成操作的方法。如果部署环境是Java 5,可以采用ConcurrentHashMap。

还有几个支持高级并发设计的类。以下是其中的一小部分,如表13-1所示。

表13-1 支持高级并发设计的类(部分)

类作  用
ReentrantLock可在一个方法中获取、在另一个方法中释放的锁
Semaphore经典的“信号”的一种实现,有计数器的锁
CountDownLatch在释放所有等待的线程之前,等待指定数量事件发生的锁。这样,所有线程都平等地几乎同时启动

建议:检读可用的类。对于Java,需要掌握java.util.concurrent、java.util. concurrent.atomic和java.util.concurrent.locks。

13.5 了解执行模型

有几种在并发应用中切分行为的途径。要讨论这些途径,我们需要理解一些基础定义,如表13-2所示。

表13-2 基础定义

定  义描  述
限定资源并发环境中有着固定尺寸或数量的资源。例如,数据库连接和固定尺寸读/写缓存等
互斥每一时刻仅有一个线程能访问共享数据或共享资源
线程饥饿一个或一组线程在很长时间内或永久被禁止。例如,总是让执行得快的线程先运行,假如执行得快的线程没完没了,则执行时间长的线程就会“挨饿”
死锁两个或多个线程互相等待执行结束。每个线程都拥有其他线程需要的资源,如果得不到其他线程拥有的资源,就无法终止
活锁执行次序一致的线程,每个都想要起步,但发现其他线程已经“在路上”。由于竞步的原因,线程会持续尝试起步,但在很长时间内却无法如愿,甚至永远无法启动

有了这些定义,我们就能讨论在并发编程中用到的几种执行模型了。

13.5.1 生产者-消费者模型

一个或多个生产者线程创建某些工作,并置于缓存或队列中。一个或多个消费者线程从队列中获取并完成这些工作。生产者和消费者之间的队列是一种限定资源。

13.5.2 读者-作者模型

当存在一个主要为读者线程提供信息源,但只偶尔被作者线程更新的共享资源,吞吐量就会是个问题。增加吞吐量,会导致线程饥饿和过时信息的累积。更新会影响吞吐量。协调读者线程,不去读作者线程正在更新的信息(反之亦然),这是一种辛苦的平衡工作。作者线程倾向于长期锁定许多读者线程,从而导致吞吐量问题。

挑战之处在于平衡读者线程和作者线程的需求,实现正确操作,提供合理的吞吐量,避免线程饥饿。

13.5.3 宴席哲学家

想象一下,一群哲学家环坐在圆桌旁。每个哲学家的左手边放了一把叉子。桌面中央摆着一大碗意大利面。哲学家们思索良久,直至肚子饿了。每个人都要拿起叉子吃饭。但除非手上有两把叉子,否则就没法进食。如果左边或右边的哲学家已经取用一把叉子,中间这位就得等到别人吃完、放回叉子。每位哲学家吃完后,就将两把叉子放回桌面,直到肚子再饿。

用线程代替哲学家,用资源代替叉子,就变成了许多企业级应用中进程竞争资源的情形。如果没有用心设计,这种竞争式系统就会遭遇死锁、活锁、吞吐量和效率降低等问题。

你可能遇到的并发问题,大多数都是以上3种模型的变种。请研究并使用这些算法,这样,遇到并发问题时你就能有解决问题的准备了。

建议:学习这些基础算法,理解其解决方案。

13.6 警惕同步方法之间的依赖

同步方法之间的依赖会导致并发代码中的狡猾缺陷。Java语言有synchronized概念,可以用来保护单个方法。然而,如果在同一共享类中有多个同步方法,系统就可能写得不太正确了。

建议:避免使用一个共享对象的多个方法。

有时必须使用一个共享对象的多个方法。在这种情况发生时,有以下3种写对代码的手段:

  • 基于客户端的锁定——客户端代码在调用第一个方法前锁定服务端,确保锁的范围覆盖了调用最后一个方法的代码;

  • 基于服务端的锁定——在服务端内创建锁定服务端的方法,调用所有方法,然后解锁。让客户端代码调用新方法;

  • 适配服务端——创建执行锁定的中间层。这是一种基于服务端的锁定的例子,但不修改原始服务端代码。

13.7 保持同步区域微小

关键字synchronized制造了锁。同一个锁维护的所有代码区域在任一时刻保证只有一个线程执行。锁是昂贵的,因为它们带来了延迟和额外开销。所以我们不愿将代码扔给synchronized语句了事。另外,临界区应该被保护起来。所以,应该尽可能少地设计临界区。

有些天真的程序员想通过扩大临界区面积达到这个目的。然而,如果将同步延展到最小临界区范围之外,会加剧资源争用,降低执行效率。

建议:尽可能减小同步区域。

13.8 很难编写正确的关闭代码

编写永远运行的系统,与编写运行一段时间后平静地关闭的系统是两码事。

平静关闭很难做到。常见问题与死锁有关,线程一直等待永远不会到来的信号。

例如,想象一个系统中有一个父线程分裂出数个子线程,父线程等待所有子线程结束后才会释放资源并关闭。如果其中一个子线程发生死锁会怎样?父线程将一直等待下去,而系统就永远不能关闭。

或者,考虑一个被指示关闭的类似系统。父线程告知全体子线程放弃任务并结束。如果其中两个子线程正以生产者-消费者模型操作会怎样呢?假设生产者线程从父线程处接收到信号,并迅速关闭,而消费者线程可能还在等待生产者线程发来消息,于是就被锁定在无法接收到关闭信号的状态中,它会死等生产者线程,永不结束,从而导致父线程也无法结束。

这类情形并非不常见。如果你要编写涉及平静关闭的并发代码,就多预留一些时间搞对关闭过程。

建议:尽早考虑关闭问题,尽早令其工作正常。这会花费比你预期的更多的时间。检视既有算法,因为这可能会比想象中难得多。

13.9 测试线程代码

证明代码的正确性不切实际。测试并不能确保正确性,然而,好的测试却能尽量降低风险。这对于所有单线程解决方案都是对的。当有两个或多个线程使用同一代码段和共享数据,事情就变得非常复杂了。

建议:编写有潜力曝露问题的测试,在不同的编程配置、系统配置和负载条件下频繁运行。如果测试失败,就跟踪错误,别因为后来测试通过了后来的运行就忽略失败。

有一大堆问题要考虑。下面是一些精练的建议:

  • 将伪失败看作可能的线程问题;

  • 先使非线程代码可工作;

  • 编写可插拔的线程代码;

  • 编写可调整的线程代码;

  • 运行多于处理器数量的线程;

  • 在不同平台上运行;

  • 调整代码并强迫错误发生。

13.9.1 将伪失败看作可能的线程问题

线程代码导致“不可能失败的”失败。多数开发者缺乏有关线程如何与其他代码(可能由其他作者编写)互动的直觉。线程代码中的缺陷可能在一千或一百万次执行中才会显现一次。重复执行想要复现问题令人沮丧。所以开发者常常会将失败归咎于宇宙射线、硬件错误或其他“偶发事件”。最好假设这种偶发事件根本不存在。“偶发事件”被忽略得越久,代码就越有可能搭建于不完善的基础之上。

建议:不要将系统错误归咎于偶发事件。

13.9.2 先使非线程代码可工作

这看起来太浅显,但强调一下不无益处。确保线程之外的代码可工作,通常,这意味着创建由线程调用的POJO。POJO与线程无涉,所以可在线程环境之外测试。能放进POJO中的代码越多越好。

建议:不要同时追踪非线程缺陷和线程缺陷。确保代码在线程之外可工作。

13.9.3 编写可插拔的线程代码

编写可在数个配置环境下运行的线程代码:

  • 单线程与多个线程在执行时不同的情况;

  • 线程代码与实物或测试替身互动;

  • 用运行快速、缓慢和有变动的测试替身执行;

  • 将测试配置为能运行一定数量的迭代。

建议:编写可插拔的线程代码,这样就能在不同的配置环境下运行。

13.9.4 编写可调整的线程代码

要获得良好的线程平衡,常常需要反复试验。一开始,在不同的配置环境下监测系统性能。要允许线程数量可调整。在系统运行时允许线程发生变动。允许线程依据吞吐量和系统使用率自我调整。

13.9.5 运行多于处理器数量的线程

系统在切换任务时会发生一些事。为了促使任务交换的发生,运行多于处理器或处理器核心数量的线程。任务交换越频繁,越有可能找到错过临界区或导致死锁的代码。

13.9.6 在不同平台上运行

2007年,我们开发了一套关于并发编程的课程。该课程主要在OS X下开发,在运行于虚拟机的Windows XP上展示。对于用于演示的测试失败条件,在OS X上要比在XP上失败得更频繁。

被测试的代码已知是不正确的。这正强调了不同操作系统有着不同线程策略的事实,不同的线程策略影响了代码的执行。在不同环境中,多线程代码的行为也不一样。应该在所有可能部署的环境中运行测试。

建议:尽早并经常地在所有目标平台上运行线程代码。

13.9.7 装置试错代码

并发代码中藏有缺陷,这并不罕见。简单的测试往往无法曝露这些缺陷。实际上,缺陷经常隐藏于一般处理过程中,可能好几个小时、好几天甚至好几个星期才会跳出来一次!

线程中的缺陷之所以如此不频繁、偶发、难以重现,是因为在几千个穿过脆弱区域的可能路径当中,只有少数路径会真的导致失败。经过会导致失败的路径的可能性惊人地低。所以,监测与调试也非常难。

怎么才能增加捕捉住如此罕见之物的机会?可以装置代码,增加对Object.wait()、Object.sleep()、Object.yield()和Object.priority()等方法的调用,改变代码执行顺序。

这些方法都会影响执行顺序,从而增加监测到缺陷的可能性。有问题的代码最好尽早测试,尽可能多地使其通不过测试。

有两种装置代码的方法:

  • 硬编码;

  • 自动化。

13.9.8 硬编码

你可以手工向代码中插入wait()、sleep()、yield()和priority()的调用。在测试某段棘手的代码时,正当如此操作。

下面是一个例子:

public synchronized String nextUrlOrNull() {
  if(hasNext()) {
    String url = urlGenerator.next();
    Thread.yield(); // inserted for testing.
    updateHasNext();
    return url;
  } 
  return null;
}

插入对yield()的调用,将改变代码的执行路径,由此可能导致代码在以前未失败过的地方失败。如果代码的确出错,那并非是因为你插入了yield()方法调用。代码出错了,才是失败的原因。

这种方法有许多毛病:

  • 你得手工找到合适的地方来插入方法调用;

  • 你怎么知道在哪里插入调用、插入什么调用?

  • 不必要地在产品环境中留下这类代码,将拖慢代码执行速度;

  • 这是一种无的放矢的手段。你可能找不到缺陷。实际上,这不在你的把握之中。

我们所需要的,是一种在测试中但不在生产中实现的手段。我们还需要为多次运行方便地调整配置,从而增加总的发现错误的机会。

无疑,如果将系统分解为对线程及控制线程的类一无所知的POJO,就能更容易地找到装置代码的位置,而且,还能创建许多个以不同方式调用sleep、yield等方法的POJO测试。

13.9.9 自动化

可以使用Aspect-Oriented Framework、CGLIB或ASM之类的工具,通过编程来装置代码。例如,可以使用有单个方法的类:

public class ThreadJigglePoint {
  public static void jiggle() {
  }
}

可以在代码的不同位置调用这个方法:

public synchronized String nextUrlOrNull() {
  if(hasNext()) {
    ThreadJigglePoint.jiggle();
    String url = urlGenerator.next();
    ThreadJigglePoint.jiggle();
    updateHasNext();
    ThreadJigglePoint.jiggle();
    return url;
  } 
  return null;
}

如此,你就得到了一个随机选择无所作为、睡眠或让步的方面。

或者,想象ThreadJigglePoint类有两种实现。第一种实现jiggle什么都不做,在生产环境中使用。第二种实现生成一个随机数,在睡眠、让步或径直执行间做选择。如果上千次地做这种随机测试,大概就能找到一些缺陷的根源。假如测试都通过了,至少你可以说自己已谨慎对待。这种方法看似过于简单,却是替代复杂工具的一种可选方案。

有一种叫作ConTest的工具,由IBM公司开发,能做类似的事情,但做法稍微复杂些。

要点是让代码发生“异动”,从而使线程以不同次序执行。编写良好的测试与“异动”组合,能有效地增加发现错误的机会。

建议:使用异动策略搜出错误。

13.10 小结

并发代码很难写正确。加入多线程和共享数据后,简单的代码也会变成噩梦。要编写并发代码,就得严格地编写整洁的代码,否则将面临细微和不频繁发生的失败。

第一要诀是遵循单一权责原则。将系统切分为分离了线程相关代码和线程无关代码的POJO。确保在测试线程相关代码时只是在测试,而没有做其他事情。线程相关代码应该保持短小和目的集中。

了解并发问题的可能原因:对共享数据的多线程操作,或使用了公共资源池。类似平静关闭或停止循环之类的边界情况尤其棘手。

学习类库,了解基本算法。理解类库提供的与基础算法类似的解决问题的特性。

学习如何找到必须锁定的代码区域并锁定之。不要锁定不必锁定的代码。避免从锁定区域中调用其他锁定区域。这需要深刻理解某物是否已共享。尽可能减少共享对象和共享范围。修改对象的设计,向客户代码提供共享数据,而不是迫使客户代码管理共享状态。

问题会跳出来。那种在早期没跳出来的问题往往是偶发的。这种所谓的偶发问题,通常仅在高负载下出现或者偶然出现。所以,你需要在不同平台上,以不同配置持续重复运行线程代码。跟随TDD三要则而来的可测试性意味着某种程度的可插拔性,从而提供了在大量不同配置下运行代码的必要支持。

如果花点时间装置代码,就能极大地提升发现错误代码的机会。可以手工做,也可以使用某种自动化技术。尽早这么做。在将线程代码投入生产环境前,就要尽可能多地运行它。

只要采用了整洁的做法,做对的可能性就会有翻天覆地的提高。

科技中介服务机构在协助企业对接科研成果时,如何提升服务响应速度与专业化水平.docx 科技中介服务机构在协助企业对接科研成果时,如何提升服务响应速度与专业化水平 立即下载

相关推荐

国央企创新负责人在推动内部技术成果转化与外部协同创新时,如何实现资源的高效整合与协同联动?.docx

国央企创新负责人在推动内部技术成果转化与外部协同创新时,如何实现资源的高效整合与协同联动?

蒙学数据集全解析:chinese-poetry 仓库传统启蒙读物 JSON 结构指南

> **导读**:本文以 [蒙学/README.md](https://link.gitcode.com/i/6b812d83dcf9f5407930f75fedc268e5) 为主体,系统解析 chinese-poetry 仓库中「蒙学」目录下 12 部中国传统儿童启蒙读物的收录背景、版本信息与 JSON 数据格式。读完本文,你将掌握「三百千」等蒙学经典的字段结构与三类嵌套层级模式,能够直接读取

gitblog_00865的博客 304

机器人入门实战:用 ROS2 和 Gazebo 先把差速车 URDF 跑起来,再做一版 SLAM 建图(含教学手册与源码骨架)

原创教学资源包(VIP专属)。围绕机器人开发项目实战搭建可复现闭环:用差速车 URDF 把 base_link、左右轮和激光连杆的关节树写自洽,配一间教学库房的 Gazebo 世界与 launch 骨架;再提供一版合成二维占用栅格建图,对比纯里程计写入和扫描匹配修正,用来分辨 odom 系漂移与 map 系 SLAM 输出。内含中文实战手册与可 make 抽检的 ROS 2 源码骨架。实现均为教学桩,注释标明不是完整 Navigation2 栈,不能替代实机传感器标定,也不是 Cartographer / slam_toolbox 的生产方案。 使用方法:解压后进入 src/ 目录(需 Linux、Python 3;不必安装 ROS 2 或 Gazebo 也能抽检)。先执行 make clean && make test,或运行 python3 scripts/smoke_test.py,看到 ALL SMOKE TESTS PASSED 后再阅读手册.md、README.md 与源码注释。若本机已安装 ROS 2 Humble/Jazzy 与 Gazebo,再把 src/diffbot_slam_tutor 放入工作空间,按 README 中「读者本地环境再跑」的 launch 命令启动仿真。细节见手册.md 与 src/README_SRC.txt。 适合作为成长任务「机器人开发项目实战」的自制合规 VIP 资源上传。建议售价 9.90 元。模型与数据均为虚构示例,不含刷量、搬运或真实个人信息。

如何让 Root 安卓通过 Play Integrity 设备认证:PlayIntegrityFix 模块完整实战

PlayIntegrityFix 是一个面向 Root 安卓设备(Android 8–15)的 Zygisk 模块,用于修复已解锁 bootloader 设备的 Play Integrity(含 SafetyNet)认证结果。它只在 GMS 校验进程内注入一份"干净的设备报表",不改动系统其他部分。 ## 先搞懂原理:它只伪装"校验进程",而不是整机 用一句话概括机制:模块借助 Zygisk(

gitblog_00787的博客 187

两路半桥LLC谐振变换器软开关Simulink仿真(模拟谐振参数不同、分裂电容)

内容概要:本文介绍了“两路半桥LLC谐振变换器软开关Simulink仿真”资源包,重点在于基于Simulink平台对两路半桥LLC谐振变换器进行高精度建模与动态仿真,特别考虑了谐振参数不一致及分裂电容等非理想工况对系统性能的影响。该仿真模型不仅涵盖了主电路拓扑、软开关实现机制与谐振网络设计,还深入分析了不同参数配置下系统的启动特性、稳态效率、动态响应及软开关条件的满足情况。通过参数扫描与对比仿真,帮助用户掌握LLC变换器的关键设计要素与优化方法,具备较强的工程指导价值和科研教学意义。; 适合人群:电力电子、电气工程及其自动化等相关专业的高校师生,以及从事高频开关电源、新能源变换器、车载充电机等领域的研发工程师和技术人员。; 使用场景及目标:① 掌握两路并联半桥LLC谐振变换器的建模与仿真方法;② 理解软开关技术在提升变换器效率与降低EMI方面的关键作用;③ 分析谐振参数偏差与分裂电容结构对系统均流特性、增益曲线及软开关范围的影响;④ 支持科研项目中的拓扑验证、控制策略开发与故障工况分析; 阅读建议:建议结合Simulink仿真模型与配套说明文档进行学习,重点关注谐振腔参数设置、驱动时序设计及软开关波形观测;可通过调整谐振电感、电容值或引入参数失配,观察系统行为变化,深入理解LLC变换器的稳定边界与优化设计原则。

双向全桥CLLC谐振变换器仿真模型(Simulink仿真实现)

内容概要:本文系统介绍了双向全桥CLLC谐振变换器的Simulink仿真模型,重点阐述了变频控制、闭环仿真及其与多种电力电子拓扑(如维也纳整流器、DAB变换器、图腾柱PFC等)的联合仿真设计。资源包涵盖2.5kW双向全桥LLC、V2G电动汽车充放电系统、三相PFC、多相交错LLC等多种变换器的仿真案例,并深入展示了三电平VIENNA整流器在双闭环PI控制与SVPWM调制策略下,结合上下电容电压差值补偿实现中点电位平衡的先进控制方法。通过仿真验证,该策略不仅有效维持了800V直流母线电压的稳定,还在动态工况下显著提升了电容电压的均衡度,同时保持了较低的输入电流THD,充分体现了SVPWM与中点平衡控制协同优化的技术优势。; 适合人群:具备电力电子与自动控制理论基础,从事新能源发电、电动汽车、工业电源、微电网等领域的研究生、工程师及科研人员。; 使用场景及目标:① 学习并构建高频谐振变换器(如LLC、CLLC)的Simulink仿真模型,掌握其变频与混合控制策略;② 深入理解并应用中点电位平衡、双闭环PI控制、SVPWM调制等核心技术于VIENNA等三电平变换器中;③ 复现高水平期刊论文中的控制算法,服务于科研项目申报、学术论文撰写或工程产品开发。; 阅读建议:该资源为综合性仿真资料库,建议使用者结合自身研究方向(如电动汽车充电、新能源并网)选择对应案例进行精学,重点关注控制策略的设计逻辑、仿真参数的整定方法以及SVPWM与中点平衡的协同机制,并充分利用附带的计算书和参考文献深化理解,以实现从仿真模型到实际工程应用的顺利转化。

PPT Master 常见问题深度解析:从源文件输入、安装更新到动画图表与模板复用的全生命周期指南

本文基于 [PPT Master](https://link.gitcode.com/i/a3b598febd79952cd24c6e9cf764c31b) 官方 FAQ([docs/zh/faq.md](https://link.gitcode.com/i/8efa599b9c55285b9e5e9b596546a800))系统展开,围绕「把任何资料变成原生可编辑 PowerPoint」这条主线

gitblog_00020的博客 1077

全桥LLC谐振(变频+移相混合控制)变换器闭环仿真模型(Simulink仿真实现)

全桥LLC谐振(变频+移相混合控制)变换器闭环仿真模型(Simulink仿真实现)

Linux | Bash Shebang 语法与结构及常见问题排查

……

u013669912的博客 1161

虚拟同步发电机(VSG)惯量阻尼自适应控制仿真模型(simulink仿真实现)

内容概要:本文提供了“虚拟同步发电机(VSG)惯量阻尼自适应控制仿真模型”的Simulink仿真实现方法,系统阐述了如何通过仿真手段研究VSG在电力系统中的动态响应特性,重点聚焦于其惯量与阻尼的自适应控制策略。文中详细说明了模型构建流程,涵盖控制算法设计、参数调节、根轨迹分析等内容,并通过对比不同调节系数下的系统响应,深入探讨了控制参数对系统稳定性与动态性能的影响。该仿真模型有效复现了VSG模拟传统同步发电机惯性与阻尼特性的能力,对于提升新能源并网系统的频率稳定性和抗扰动能力具有重要研究价值。; 适合人群:具备电力系统、自动化或相关领域基础知识,从事新能源并网、微电网控制、电力电子与电力系统仿真研究的研究生、科研人员及工程师。; 使用场景及目标:①用于教学与科研中深入理解虚拟同步发电机的工作原理及其在现代电力系统中的应用;②为新能源发电系统接入电网时的稳定性分析与控制策略设计提供仿真验证平台;③支持对VSG控制参数进行优化,提升系统应对负荷波动和扰动的能力。; 阅读建议:建议读者结合Matlab/Simulink软件动手实践,按照文档结构逐步搭建仿真模型,重点关注控制环路设计与参数整定部分,同时参考提供的代码与仿真结果进行对比分析,以加深对VSG自适应控制机制的理解。

两相交错并联Boost电路仿真模型(Simulink仿真实现)

内容概要:本文介绍了“两相交错并联Boost电路仿真模型(Simulink仿真实现)”的技术资源,属于电力电子变换器领域的仿真研究内容。该模型基于Simulink平台构建,旨在深入分析两相交错并联Boost电路的工作原理、动态响应特性及控制策略。交错并联结构通过错开各相开关管的导通时刻,有效降低输入电流纹波、提升系统效率与功率密度,适用于中高功率DC-DC变换应用场景。资源包含完整的主电路建模、PWM驱动信号生成、闭环控制器设计(如电压/电流双闭环控制)等关键模块,并可能涵盖软启动、均流控制等关键技术问题。整个仿真模型强调理论分析与工程实践的紧密结合,不仅有助于理解电路基本行为,也为优化控制算法提供验证平台,服务于科研与教学需求。; 适合人群:电气工程、自动化、电力电子及相关专业的高校本科生、研究生,以及从事电源系统研发的工程技术人员,具备一定的电路理论、自动控制原理和Simulink仿真基础者更佳。; 使用场景及目标:① 学习和掌握两相交错并联Boost电路的拓扑结构、工作机理及性能优势;② 利用Simulink进行电力电子电路的建模与仿真,深入理解PWM控制、闭环调节、均流控制等核心技术的应用;③ 为科研项目、课程设计或毕业设计提供可复用的仿真案例和技术支持,辅助开展中高功率电源系统的创新设计与优化研究。; 阅读建议:建议结合Simulink软件动手搭建并调试模型,通过调整占空比、开关频率、电感参数等变量,观察其对输入电流纹波、输出电压稳定性及动态响应的影响,以深化对电路特性的理解;同时可参考文中列出的其他相关资源(如LLC、PFC等仿真模型)进行横向对比学习,拓宽电力电子系统设计视野。

Legal RAG:基于 LangChain 的轻量化法律智能问答系统

面向中国法律领域的检索增强生成(RAG)系统:以 LangChain 为编排框架,通过硅基流动(SiliconFlow) 云端 API 调用 DeepSeek-V3.2 生成模型与 BGE-M3 嵌入模型,结合 ChromaDB 向量数据库,实现了从文档加载、智能分块、向量化存储到语义检索与生成的完整 RAG 流程。

m0_59777389的博客 227

区域电网含风光火压缩空气储能抽水蓄能以及电化学储能的联合调度,加入IEEE39系统,进行潮流计算,分析电能质量研究(Matlab代码实现)

内容概要:本文研究了包含风能、太阳能、火电以及压缩空气储能、抽水蓄能和电化学储能等多种能源形式的区域电网联合调度问题,通过将系统接入IEEE 39节点标准测试系统,利用Matlab平台开展潮流计算与电能质量分析,旨在提升高比例可再生能源接入背景下电网运行的稳定性与供电品质。研究重点涵盖多类型电源与多种储能系统的协调优化调度策略设计,深入探讨储能系统在削峰填谷、平滑新能源出力波动方面的关键作用,并对复杂运行工况下电压偏差、频率波动、谐波污染等电能质量问题进行评估与改善。通过构建完整的仿真模型,系统性地分析了联合调度对电网电能质量的影响机制,为现代电力系统中多能互补与协同控制提供了理论依据和技术参考。; 适合人群:具备电力系统分析、优化调度及电能质量相关基础知识,熟悉Matlab/Simulink仿真环境的研究生、科研人员及电力系统相关领域的工程师。; 使用场景及目标:①为含多种储能的区域电网联合调度研究提供完整的Matlab仿真案例与开源代码参考;②分析大规模新能源接入对IEEE 39系统潮流分布及电能质量的综合影响,并定量评估不同类型储能系统的调控效果;③为电力系统优化调度、电能质量综合治理、多能协同控制等学术研究与工程项目提供可复现的技术支撑与解决方案。; 阅读建议:本资源以Matlab代码实现为核心,读者应在掌握电力系统基本理论的基础上,重点关注联合调度的数学建模过程、储能系统充放电策略的制定逻辑以及电能质量各项评价指标的计算方法。建议在学习过程中动手调试仿真代码,尝试调整系统参数、优化目标权重或改变新能源渗透率,以深入理解系统动态行为与控制策略的有效性。

Flutter项目中数据统计报表开发的工程化实践与代码规范

在移动应用开发中,数据统计报表与BI功能往往承载着业务分析的核心需求。然而,这类模块开发过程中常伴随结构混乱、逻辑耦合等问题,尤其是在团队协作场景下,如何通过工程化手段和代码规范提升代码质量,成为每个技术负责人需要面对的挑战。本文以Flutter为技术栈,结合一个完整的数据统计报表项目为例,从实际开发流程出发,深入探讨如何通过规范化配置、可复用组件设计和自动化工具集成等方式,系统性提升团队代码质量。

wuchangjian的专栏 279

2026最新基于Aria2下载器解决百度网盘限速问题

Aria下载器

envi反演水质参数_遥感水质反演之反演模型

水质反演的框架图此为反演模型的章节第一部分,统计学基础:概率论、矩阵和数理统计的知识统计学知识框架# 1 基本统计概念知识在于应用,而非记忆!我们都是在样本的层面去操作数据的,以达到对总体性质的估计和推断,拿到一组数据首先进行基本的统计描述,常用的有:样本均值: 样本方差(调整方差)不是除以n,而是n-1, 标准差为样本方差开方即可皮尔逊积矩相关系数(简称相关系数): 相关系数范围在[-1,...

weixin_35275922的博客 3229
上一篇: 【Code】《代码整洁之道》笔记-Chapter12-迭进
下一篇: 【Code】《代码整洁之道》笔记-Chapter14-逐步改进
江湖人称菠萝包
博客等级 码龄10年 762粉丝 314原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值