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

并发工具类(二):CyclicBarrier

1、CyclicBarrier 介绍

      从字面上看 CyclicBarrier 就是 一个循环屏障,它也是一个同步助手工具,它允许多个线程

      在执行完相应的操作后彼此等待共同到达一个屏障点。

      CyclicBarrier可以被循环使用,当屏障点值变为0之后,可以在接下来的的使用中重置屏障点

      值,而无需重新定义一个CyclicBarrier。

      Cyclic循环:所有线程释放后,屏障点的数值可以被重置

       Barrier屏障:让一个或多个线程到达一个屏障点,会被阻塞;屏障点会有一个数值,当每有一

                           个线程到达屏障点时,屏障点数值就会减1操作,并且线程阻塞在屏障点,当屏

                            障点数值变为0时,屏障就会打开,唤醒所有阻塞在屏障点的线程。
                            在释放屏障点之后,可以先执行一个任务,然后让唤醒阻塞的线程继续执行后

                            续任务。 

         CyclicBarrier是一种同步机制,允许一组线程之间互相等待,现成达到屏障点其实是基于

         await方法在屏障点阻塞;等待所有线程到达屏障点后再统一唤醒

         CyclicBarrier 并不是基于AQS来实现的,其是基于ReentrantLock锁的机制来实现对屏障点的

         “减减” 操作以及线程的挂起。

2、CyclicBarrier核心属性&构造方法

public class CyclicBarrier {/*** 内部类*/private static class Generation {//该类用来标记是否被中断过//用来表示阻塞时当前party有没有被强制中断boolean broken = false;//某个线程由于执行了await()方法进入了阻塞状态,若该线程被执行了中断操作,那么 broken 得值就会变为true}/** 保证操作屏障值原子性的锁 */private final ReentrantLock lock = new ReentrantLock();/*** 用于阻塞线程的条件变量:若有未到party的线程,那么等待该条件变量上* 基于当前的Condition 实现线程的挂起和唤醒* */private final Condition trip = lock.newCondition();/*** 屏障数值,与count初始值一致* todo 注意:不会对 parties 进行操作,因为 parties 是final修饰,初始化后不能修改* */private final int parties;//计数器得值/** 当屏障数值count到达0时,优先执行当前任务,然后再会唤醒所有等待的线程执行后续任务* */private final Runnable barrierCommand;/*** 表示当前party是否被中断过* */private Generation generation = new Generation();/**** 屏障数值,初始值与 parties 相等,当每有一个线程到达屏障点时,就会执行count--操作*/private int count;//构造方法/*** * @param parties  屏障点数值*        * @param barrierAction  当屏障点数值达到0时,优先执行该 barrierAction 任务*                        若barrierAction 为null,则直接执行唤醒的线程*        */public CyclicBarrier(int parties, Runnable barrierAction) {if (parties <= 0) throw new IllegalArgumentException();this.parties = parties;this.count = parties;//到达屏障点后优先执行的任务this.barrierCommand = barrierAction;}public CyclicBarrier(int parties) {this(parties, null);}
}

3、CyclicBarrier应用场景及示例代码

3.1、将一个任务分成若干个并行的子任务,当所有的子任务全部执行结束后,再继续执行后边的

        工作。

        从这一点上看,CyclicBarrier 功能与CountDownLatch 的功能差不多,但他们运行方式上却

        有很大区别;在 CyclicBarrier 中,每个子任务完成后,子线程调用 CyclicBarrier的await方法

        使当前子线程进入阻塞状态,直到其他所有子线都完成了任务后,他们才能退出阻塞;

        注意:这里CyclicBarrier并没有干预主线程的运行,所以主线程的 “运行/阻塞” 需要我们来

                  手动干预。所以 CyclicBarrier 更像是把“任务分片”而不是计数器,当每个分片任务

                   完成后都会阻塞在“屏障点”,

        把前边CountDwonLatch 的示例使用 CyclicBarrier 来实现,比较 CountDwonLatch 与

        CyclicBarrier 在相同场景下使用的不同,示例代码如下:

public class CylicBarrierExample1 {public static void main(String[] args) {//先获取商品编号列表int[] products = getProductsByCategoryID();//使用Stream 流,将商品编号列表中的每个商品转换为 ProductPriceList<ProductPrice> list = Arrays.stream(products).mapToObj(ProductPrice::new).collect(Collectors.toList());//定义 CyclicBarrier ,并设置子任务数CyclicBarrier barrier = new CyclicBarrier(list.size());//存放线程任务的集合final List<Thread> threads = new ArrayList<>();list.forEach(pp -> {//对每个商品都创建一个子任务来计算Thread thread = new Thread(() -> {System.out.println(pp.getProdID()+" -> start calculate price.");try {//模拟业务逻辑耗时TimeUnit.SECONDS.sleep(current().nextInt(10));if(pp.prodID %2 == 0){pp.setPrice(pp.prodID*0.9D);}else {pp.setPrice(pp.prodID*0.71D);}System.out.println(pp.getProdID()+" -> price calculate");} catch (InterruptedException e) {e.printStackTrace();}finally {try {//当前子任务线程进入阻塞状态,在这里等待所有的子任务线程都执行到共同的屏障点 barrier pointbarrier.await();} catch (InterruptedException | BrokenBarrierException e) {e.printStackTrace();}}});threads.add(thread);thread.start();});//遍历所有的子任务线程,让主线程等待所有的子任务线程结束threads.forEach(t -> {try {t.join();} catch (InterruptedException e) {e.printStackTrace();}});System.out.println("**************************************");System.out.println("All of price calculate finished!");list.forEach(System.out::println);}//获取商品编号列表private static int[] getProductsByCategoryID(){//商品列表编号为从1,10的数字return IntStream.rangeClosed(1,10).toArray();}//定义商品类,有2个成员变量:商品编号和商品价格private static class ProductPrice{private final int prodID;//商品编号private double price;//商品价格public ProductPrice(int prodID){this(prodID,-1);}public ProductPrice(int prodID,double price){this.prodID = prodID;this.price = price;}public int getProdID(){return this.prodID;}public void setPrice(double price){this.price = price;}@Overridepublic String toString() {return "ProductPrice{" +"prodID=" + prodID +", price=" + price +'}';}}
}

        上边这段代码,有个需要优化的地方,即:既然 CyclicBarrier 中所有线程都会阻塞在屏

         障点,所有任务都达到屏障点时才会往下执行,那么我们可以把主线程也作为一个任务线程

        ,即在定义 CyclicBarrier 屏障点数值时,在原有的数值上加1,然后在主线程中执行

         CyclicBarrier的await方法,这样就不用让主线程等待每个子线程执行完成了

         优化代码如下:

public static void main(String[] args) {//先获取商品编号列表int[] products = getProductsByCategoryID();//使用Stream 流,将商品编号列表中的每个商品转换为 ProductPriceList<ProductPrice> list = Arrays.stream(products).mapToObj(ProductPrice::new).collect(Collectors.toList());//定义 CyclicBarrier ,并设置子任务数CyclicBarrier barrier = new CyclicBarrier(list.size()+1);//存放线程任务的集合final List<Thread> threads = new ArrayList<>();list.forEach(pp -> {//对每个商品都创建一个子任务来计算Thread thread = new Thread(() -> {System.out.println(pp.getProdID()+" -> start calculate price.");try {//模拟业务逻辑耗时TimeUnit.SECONDS.sleep(current().nextInt(10));if(pp.prodID %2 == 0){pp.setPrice(pp.prodID*0.9D);}else {pp.setPrice(pp.prodID*0.71D);}System.out.println(pp.getProdID()+" -> price calculate");} catch (InterruptedException e) {e.printStackTrace();}finally {try {//当前子任务线程进入阻塞状态,在这里等待所有的子任务线程都执行到共同的屏障点 barrier pointbarrier.await();} catch (InterruptedException | BrokenBarrierException e) {e.printStackTrace();}}});threads.add(thread);thread.start();});//主线程也阻塞在屏障点barrier.await();System.out.println("**************************************");System.out.println("All of price calculate finished!");list.forEach(System.out::println);}

         

3.2、CyclicBarrier 循环使用

        使用 CyclicBarrier 模拟旅游时导游清点人数的场景

         大家报团旅游时,为了安全和避免掉队的,每次登上大巴,大巴启动前,导游都会清点人数

         ;在到达一个景点后,游客下车后,导游也会重复清点人数,保证所有的人都下来了后,才

         会通知大巴师傅去停车场停车,下边写个demo简单模拟下这个场景,

/*** CylicBarrier 的循环使用*/
public class CylicBarrierExample2 {public static void main(String[] args) throws BrokenBarrierException, InterruptedException {//定义 CyclicBarrierfinal CyclicBarrier barrier = new CyclicBarrier(11);//创建10个线程for(int i=0;i<10;i++){//定义游客子线程,传入游客编号和 barriernew Thread(new Tourist(i,barrier)).start();}//主线程也进入阻塞,等待所有游客都上车barrier.await();System.out.println("Tour Guilder: all of Tourist get on the bus");//主线程进入阻塞,所有游客都下车barrier.await();System.out.println("Tour Guilder: all of Tourist get OFF the bus");}//定义游客线程private static class Tourist implements  Runnable{private final int touristID;private final CyclicBarrier barrier;private Tourist(int touristID,CyclicBarrier barrier){this.touristID = touristID;this.barrier = barrier;}@Overridepublic void run() {System.out.printf("Tourist: %d by bus\n",touristID);//上车耗时this.spendSeveralSeconds();//上车后等待其他同伴this.waitAndPrint("Tourist: %d Get on the bus, and wait other people");//todo 注意:所有线程到达屏障点后,最后一个到达屏障点的线程会重置CyclicBarrier//          所以这里不需要手动调用reset()重置方法//下车耗时this.spendSeveralSeconds();//下车后等待其他同步全部下车this.waitAndPrint("Tourist: %d Get OFF the bus, and wait other people OFF");}//模拟乘客上车耗时private void spendSeveralSeconds(){try {TimeUnit.SECONDS.sleep(current().nextInt(10));} catch (InterruptedException e) {e.printStackTrace();}}//模拟上车后等待其他同伴private void waitAndPrint(String msg){System.out.printf(msg,touristID);System.out.println();try {//所有线程到达屏障点后,最后一个到达屏障点的线程会重置CyclicBarrierbarrier.await();} catch (InterruptedException |BrokenBarrierException e) {e.printStackTrace();}}}
}

4、CyclicBarrier 常用方法解析

      在 CyclicBarrier 中常用方法就2个,即:await 和带超时时间的await ,但真正执行业务

      的方法其实只有 doawait 一个方法,如下图所示:

              

               

4.1、dowait(boolean timed, long nanos) 方法

         dowait 方法是CyclicBarrier 的核心方法,该方法功能是先将 CyclicBarrier 计数器count减1,

          然后判断减1后的count是否等于0,若等于0,则唤醒所有阻塞在屏障点的线程,并重置

          CyclicBarrier;若减1后的count不等于0,则当前线程被阻塞,直到被其他线程唤醒或过了

          超时时间(有超时时间的情况)

          dowait 代码如下:  

private int dowait(boolean timed, long nanos)throws InterruptedException, BrokenBarrierException,TimeoutException {final ReentrantLock lock = this.lock;lock.lock();try {//获取 Generation 对象的引用final Generation g = generation;//判断是否有现成中断if (g.broken) //表示当前party已经被中断throw new BrokenBarrierException();//有中断的线程混入其中,则干掉其他所有的线程重新开始if (Thread.interrupted()) {//判断当前执行线程是否被中断,若被中断则先调用 breakBarrier()方法,再抛出异常breakBarrier();throw new InterruptedException();}int index = --count;//计数器减1,对屏障点数据做--操作if (index == 0) {  // tripped  当 count 为0 得时候,表示是最后一个线程,负责唤醒所有阻塞在条件变量上的线程,然后回调barrierCommandboolean ranAction = false;try {final Runnable command = barrierCommand;//当前任务线程//优先执行 barrierCommand 任务if (command != null)command.run();ranAction = true;//生成新的 Generation,并且直接返回nextGeneration();//进入下一个party,这时屏障值被重置了,等价与调用了reset()方法return 0;} finally {//如果 barrierCommand 方法发生了异常,则设置 broKen标志位if (!ranAction)//中断当前任务线程breakBarrier();}}// loop until tripped, broken, interrupted, or timed outfor (;;) {//循环等待最后一个参与party的线程,或者被中断、等待超时try {if (!timed) //表示调用的是非超时时间的await方法,则这里也是调用Condition的不带超时时间的awaittrip.await();else if (nanos > 0L)//表示调用的是带超时时间的await方法nanos = trip.awaitNanos(nanos);} catch (InterruptedException ie) {//执行到这,说明线程被中断了//g == generation:查看 generation 是否被重置//若 generation 没有被重置,且没有现成被中断,则调用 breakBarrier 方法执行线程中断后的操作if (g == generation && ! g.broken) {breakBarrier();throw ie;} else {//表示 generation 已经被重置或者 有线程已经被中断,则表示本次CyclicBarrier已经作废,则中断当前线程// We're about to finish waiting even if we had not// been interrupted, so this interrupt is deemed to// "belong" to subsequent execution.Thread.currentThread().interrupt();}}//执行到这里说明线程被唤醒了//查看是否因为中断唤醒,若是则抛出异常if (g.broken)throw new BrokenBarrierException();//查看当前线程是否是因为 generation 重置而被唤醒(即被reset),若是则直接返回index 数值//或者任务正常完成也会被重置if (g != generation)return index;//判断是否是因为到达超时时间被唤醒,若是则中断当前任务if (timed && nanos <= 0L) {//中断当前任务breakBarrier();throw new TimeoutException();}}} finally {lock.unlock();}}private void nextGeneration() {// signal completion of last generation  唤醒屏障点阻塞中的所有线程trip.signalAll();// set up next generation  修改 count 的值使其等于构造 CyclicBarrier 时传入的parties 值count = parties;generation = new Generation();//创建新的Generation,即生成下一代party}private void breakBarrier() {generation.broken = true; //设置为中断状态count = parties;//将计数器设置为构建 CyclicBarrer 时传入得值,即重置屏障点数值counttrip.signalAll();//唤醒其他所有等待的线程}

4.2、reset() 方法

         reset() 方法功能是重置CyclicBarrier

public void reset() {final ReentrantLock lock = this.lock;lock.lock();try {//干掉当前所有的线程breakBarrier();   // break the current generation//生成下一代partynextGeneration(); // start a new generation} finally {lock.unlock();}}

4.3、getNumberWaiting() 方法

         该方法功能是返回正在阻塞在屏障点的线程数

public int getNumberWaiting() {final ReentrantLock lock = this.lock;lock.lock();try {return parties - count;} finally {lock.unlock();}}

相关文章:

并发工具类(二):CyclicBarrier

1、CyclicBarrier 介绍 从字面上看 CyclicBarrier 就是 一个循环屏障&#xff0c;它也是一个同步助手工具&#xff0c;它允许多个线程 在执行完相应的操作后彼此等待共同到达一个屏障点。 CyclicBarrier可以被循环使用&#xff0c;当屏障点值变为0之后&#xff0c;可以在接下来…...

Spring Cloud全解析:负载均衡之Ribbon简介

Ribbon简介 Ribbon是一种客户端的软件负载均衡算法&#xff0c;将Netflix的中间层服务连接在一起&#xff0c;提供了一系列完善的配置如连接超时、重试等&#xff0c;Ribbon会自动的帮助基于某种规则(如简单轮询、随机连接等)去连接那些机器&#xff0c;也可以自定义的负载均衡…...

Kettle安装与使用指南

1. 介绍 什么是Kettle&#xff1f; Kettle&#xff0c;全称Pentaho Data Integration (PDI)&#xff0c;是Pentaho BI套件的一部分。它提供了一个可视化的ETL工具&#xff0c;允许用户通过图形界面设计复杂的数据集成流程。Kettle支持多种数据源&#xff0c;包括关系型数据库…...

教育行业解决方案:智能PPT在教育行业的创新应用

在信息化时代&#xff0c;教育行业面临着巨大的变革。随着人工智能技术的不断发展&#xff0c;传统教学方式正在被重新定义。彩漩科技作为 AI 技术的先行者&#xff0c;推出了歌者 PPT &彩漩 PPT&#xff0c;为教师、学生和家长提供了一种全新的教育体验&#xff0c;实现了…...

Matlab程序练习

Part1 1.求 [100,999] 之间能被 21整除的数的个数。 程序&#xff1a; 主文件&#xff1a;main.m clear; start_num 100; end_num 999; div_num 21; res div(start_num,end_num,div_num); fprintf("[%d,%d]之间能被%d整除的数的个数为%d个\n",start_num,end_…...

cesium可不可以改变影像底图颜色,如何给地球底图影像添加一层滤镜蒙版?

废话&#xff1a;你的球是不是很丑&#xff1f;是不是没有科技感&#xff1f;是不是没有好看的影像&#xff1f; 因果&#xff1a; 因&#xff1a;客户问&#xff0c;底图可不可以改变颜色&#xff0c;想让球更漂亮一些。 答&#xff1a;可以改变影像饱和度&#xff0c;透明度…...

MyBatis-MappedStatement什么时候生成?QueryWrapper如何做到动态生成了SQL?

通过XML配置的MappedStatement 这部分MappedStatement主要是由MybatisXMLMapperBuilder进行解析&#xff0c;核心逻辑如下&#xff1a; 通过注解配置的MappedStatement 核心逻辑就在这个里面了&#xff1a; 继承BaseMapper的MappedStatement 我们看看这个类&#xff0c;里…...

Netty系列-2 NioServerSocketChannel和NioSocketChannel介绍

背景 本文介绍Netty的通道组件NioServerSocketChannel和NioSocketChannel&#xff0c;从源码的角度介绍其实现原理。 1.NioServerSocketChannel Netty本质是对NIO的封装和增强&#xff0c;因此Netty框架中必然包含了对于ServerSocketChannel的构建、配置以及向选择器注册&am…...

智能客服的四大优势,提升企业服务效率

在这个信息化快速发展的时代&#xff0c;客户服务的重要性越来越凸显。传统的客服方式已经无法满足企业日益增长的服务需求&#xff0c;于是智能客服服务应运而生。智能客服服务不仅改变了企业与客户的互动方式&#xff0c;还提高了服务效率和客户满意度。本文将深入探讨智能客…...

AutoGPT开源项目解读

AutoGPT开源项目解读 (qq.com) AutoGPT旨在创建一个自动化的自我改进系统&#xff0c;能够自主执行和学习各种任务 项目基本信息 首先阅读项目的README.md&#xff0c;下述代理和智能体两个名词可互换 项目简介&#xff1a;一个创建和运行智能体的工具&#xff0c;这些智能体…...

Linux离线安装fontconfig

Linux离线下载yum包&#xff0c;安装字体库 一、下载安装包 以CentOS Linux release 7.9.2009下载fontconfig的rpm包的为例 http://mirror.centos.org/centos/7/按提示跳转历史库 找到对应版本的centos https://vault.centos.org/7.9.2009/os/x86_64/Packages/在Packages目…...

海山数据库(He3DB)+AI:(一)神经网络基础

文章目录 1 引言2 基本结构2.1 神经元2.2 模型结构 3 训练过程3.1 损失函数3.2 反向传播3.3 基于梯度的优化算法 4 总结 1 引言 神经网络可以被视为一个万能的拟合器&#xff0c;通过深层的隐藏层实现输入数据到输出结果的映射。神经网络的思想源于对大脑的模拟&#xff0c;在…...

CSS中选择器有哪些?(史上最全选择器)

CSS选择器是用来选择和应用样式到HTML元素上的工具。以下是所有主要的CSS选择器的详细分类和描述&#xff1a; 1. 基本选择器 通配符选择器 (*)&#xff1a;选择所有元素。例如&#xff0c;* { color: red; } 会将所有元素的文字颜色设置为红色。元素选择器&#xff1a;选择指…...

本地部署 AI 智能体,Dify 搭建保姆级教程(下):知识库 RAG + API 调用,我捏了一个红楼解读大师

话接上篇&#xff1a; 本地部署 AI 智能体&#xff0c;Dify 搭建保姆级教程&#xff08;上&#xff09;&#xff1a;工作流 Agent&#xff0c;把 AI 接入个人微信 相信大家已经在本地搭建好 Dify 了。 今日分享&#xff0c;继续介绍 Dify 的另外两项重要功能&#xff1a; 知…...

HarmonyOS应用开发者高级认证,Next版本发布后最新题库 - 答案纯享版

这篇文章是高级题库答案纯享版&#xff0c;只有需要选择的选项。如果需要查看所有选项&#xff0c;可以点击下方链接跳转。以考代学&#xff0c;还是推荐点击下方链接&#xff0c;查看完整的题库&#xff0c;边看边学习鸿蒙应用开发。此题库已更新完毕&#xff0c;笔者将不继续…...

基于PHP的文件包含介绍

引言&#xff1a;在实际开发过程中&#xff0c;经常会遇到部分模块功能需要重复使用的情况&#xff0c;比如数据库的增删改查&#xff0c;文件包含通过将需要重复使用的功能模块代码引入其他文件的内容&#xff0c;实现重用代码、分离配置等。然而&#xff0c;如果文件包含操作…...

K7系列FPGA多重启动(Multiboot)

Xilinx 家的 FPGA 支持多重启动功能&#xff08;Multiboot&#xff09;&#xff0c;即可以从多个 bin 文件中进行选择性加载&#xff0c;从而实现对系统的动态更新&#xff0c;或系统功能的动态调整。 这一过程可以通过嵌入在 bit 文件里的 IPROG 命令实现上电后的自动加载。而…...

关于武汉芯景科技有限公司的RS232通信接口芯片XJ3243EEUI开发指南(兼容MAX3243EEUI)

一、芯片引脚介绍 1.芯片引脚 2.引脚描述 二、典型应用电路 三、功能描述 1.Transmitter 通过T1&#xff0c;T2可以将TTL电平转换为RS232电平 2.Receiver 通过R1&#xff0c;R2可以将RS232电平转换为TTL电平 3.工作模式控制 4.INVALID引脚...

TreeSize Free:你的免费磁盘空间管理专家

TreeSize Free是一款专为Windows用户设计的磁盘空间分析工具。它能够帮助用户快速识别并管理那些占用大量空间的文件夹和文件。 功能亮点 快速扫描&#xff1a;TreeSize Free能够迅速扫描整个磁盘卷&#xff0c;展示所有文件夹及其子文件夹的大小&#xff0c;甚至可以细化到单…...

python办公自动化:初识`python-docx`

1.1 什么是python-docx python-docx是一个用于在Python中创建和操作Word文档的库。它提供了一组简洁的API&#xff0c;让开发者可以轻松地生成、修改、和读取Microsoft Word (.docx)文件&#xff0c;而不需要安装Microsoft Office。这使得python-docx成为办公自动化、报告生成…...

[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?

&#x1f9e0; 智能合约中的数据是如何在区块链中保持一致的&#xff1f; 为什么所有区块链节点都能得出相同结果&#xff1f;合约调用这么复杂&#xff0c;状态真能保持一致吗&#xff1f;本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里&#xf…...

eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)

说明&#xff1a; 想象一下&#xff0c;你正在用eNSP搭建一个虚拟的网络世界&#xff0c;里面有虚拟的路由器、交换机、电脑&#xff08;PC&#xff09;等等。这些设备都在你的电脑里面“运行”&#xff0c;它们之间可以互相通信&#xff0c;就像一个封闭的小王国。 但是&#…...

C++实现分布式网络通信框架RPC(3)--rpc调用端

目录 一、前言 二、UserServiceRpc_Stub 三、 CallMethod方法的重写 头文件 实现 四、rpc调用端的调用 实现 五、 google::protobuf::RpcController *controller 头文件 实现 六、总结 一、前言 在前边的文章中&#xff0c;我们已经大致实现了rpc服务端的各项功能代…...

突破不可导策略的训练难题:零阶优化与强化学习的深度嵌合

强化学习&#xff08;Reinforcement Learning, RL&#xff09;是工业领域智能控制的重要方法。它的基本原理是将最优控制问题建模为马尔可夫决策过程&#xff0c;然后使用强化学习的Actor-Critic机制&#xff08;中文译作“知行互动”机制&#xff09;&#xff0c;逐步迭代求解…...

SCAU期末笔记 - 数据分析与数据挖掘题库解析

这门怎么题库答案不全啊日 来简单学一下子来 一、选择题&#xff08;可多选&#xff09; 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘&#xff1a;专注于发现数据中…...

解决本地部署 SmolVLM2 大语言模型运行 flash-attn 报错

出现的问题 安装 flash-attn 会一直卡在 build 那一步或者运行报错 解决办法 是因为你安装的 flash-attn 版本没有对应上&#xff0c;所以报错&#xff0c;到 https://github.com/Dao-AILab/flash-attention/releases 下载对应版本&#xff0c;cu、torch、cp 的版本一定要对…...

【生成模型】视频生成论文调研

工作清单 上游应用方向&#xff1a;控制、速度、时长、高动态、多主体驱动 类型工作基础模型WAN / WAN-VACE / HunyuanVideo控制条件轨迹控制ATI~镜头控制ReCamMaster~多主体驱动Phantom~音频驱动Let Them Talk: Audio-Driven Multi-Person Conversational Video Generation速…...

免费PDF转图片工具

免费PDF转图片工具 一款简单易用的PDF转图片工具&#xff0c;可以将PDF文件快速转换为高质量PNG图片。无需安装复杂的软件&#xff0c;也不需要在线上传文件&#xff0c;保护您的隐私。 工具截图 主要特点 &#x1f680; 快速转换&#xff1a;本地转换&#xff0c;无需等待上…...

ubuntu22.04有线网络无法连接,图标也没了

今天突然无法有线网络无法连接任何设备&#xff0c;并且图标都没了 错误案例 往上一顿搜索&#xff0c;试了很多博客都不行&#xff0c;比如 Ubuntu22.04右上角网络图标消失 最后解决的办法 下载网卡驱动&#xff0c;重新安装 操作步骤 查看自己网卡的型号 lspci | gre…...

图解JavaScript原型:原型链及其分析 | JavaScript图解

​​ 忽略该图的细节&#xff08;如内存地址值没有用二进制&#xff09; 以下是对该图进一步的理解和总结 1. JS 对象概念的辨析 对象是什么&#xff1a;保存在堆中一块区域&#xff0c;同时在栈中有一块区域保存其在堆中的地址&#xff08;也就是我们通常说的该变量指向谁&…...