AQS共享模式之CyclicBarrier

概念:CyclicBarrier翻译为循环(屏障/栅栏),当一组线程到达一个屏障(同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会打开,所有被屏障拦截的线程才会继续工作。

设计目的:和CountDownLatch都为同步辅助类,因为CountDownLatch的计数器是一次性的,属于一对多,

1、【某一个线程需要其余线程执行完之后再执行 (一个等多个)】,

2、【一些线程需要在某个时刻同时执行,就像等待裁判员枪响后,才能同时起跑(多个等一个)】

所以,CyclicBarrier被设计成为计数器可循环使用,多对多。只要多个线程都达到后,自会执行接下来的事,没有CountDownLatch的一个等多个,多个等一个的现象。

源码解析:

public class CyclicBarrier {
    /**
     * Each use of the barrier is represented as a generation instance.
     * The generation changes whenever the barrier is tripped, or
     * is reset. There can be many generations associated with threads
     * using the barrier - due to the non-deterministic way the lock
     * may be allocated to waiting threads - but only one of these
     * can be active at a time (the one to which {@code count} applies)
     * and all the rest are either broken or tripped.
     * There need not be an active generation if there has been a break
     * but no subsequent reset.
     * 屏障的每次使用都表示为一个生成实例。当屏障被触发或重置时,生成就会改变。
     可以有许多代与使用barrier的线程相关联——由于锁可能以不确定的方式分配给等待线程——但是一次只能有一个是活动的({@code count}应用的那个),
     其余的要么中断,要么被触发。如果有中断,但没有随后的重置,则不需要活动代。
     */
    private static class Generation {
        boolean broken = false;
    }

    /** The lock for guarding barrier entry
    保护屏障的锁,重入锁,调用await()方法的时候会用到,只有一个线程会执行成功,所以多线程高并发的情况下CyclicBarrier的执行效率不是很高 */
    private final ReentrantLock lock = new ReentrantLock();
    /** Condition to wait on until tripped
       待跳闸的条件( Condition本身用于线程间通信,在这就是阻塞和唤醒线程用的)*/
    private final Condition trip = lock.newCondition();
    /** The number of parties
    这个就是计数器初始值,新建类的时候传入的数值会赋值给他 */
    private final int parties;
    /* The command to run when tripped 
       跳闸时要执行操作的线程*/
    private final Runnable barrierCommand;
    /** The current generation 
       当前代*/
    private Generation generation = new Generation();

    /**
     * Number of parties still waiting. Counts down from parties to 0
     * on each generation.  It is reset to parties on each new
     * generation or when broken.
       还有很多任务在等待。每一代从计数器初始值(parties)降到0。它会在每一代新创建或用尽时重置。
     */
    private int count;

    /**
     * Updates state on barrier trip and wakes up everyone.
     * Called only while holding lock.
     只在持有锁时调用,去更新屏障的状态并唤醒其他线程。(意思代表下一代,其实就是新建一个代对象重新赋值)
     */
    private void nextGeneration() {
        // signal completion of last generation
        //当计数器减少为零证明这代已经满了,唤醒所有当前代阻塞的线程,这是Condition的方法
        trip.signalAll();
        // set up next generation
        //将初始需要拦截的计数器初始值初始化给count计数器
        count = parties;
        //上一代已经结束了,就要重新开启下一代
        generation = new Generation();
    }

    /**
    这个方法就是告诉这一代线程出问题了: 屏障里面的某个线程中断了,那就唤醒所有线程,然后这个屏障拦截的所有线程都会被唤醒,然后抛出异常,并告诉这一代坏掉了
     * Sets current barrier generation as broken and wakes up everyone.
     * Called only while holding lock.
     */
    private void breakBarrier() {
        //将这代标记为true,告诉已经坏掉了
        generation.broken = true;
        //这一代重新计数
        count = parties;
        //唤醒这一代里面的所有阻塞线程
        trip.signalAll();
    }

    /**
     * Main barrier code, covering the various policies.
     主要的屏障代码,涵盖了各种策略
     */
    private int dowait(boolean timed, long nanos)
        throws InterruptedException, BrokenBarrierException,
               TimeoutException {
                   //当多线程并发情况下只有一个线程能获得锁(注意:该锁为jvm级锁,只能保证单实例有效,多进程下管理共享资源无效)
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            //获取当前代的信息
            final Generation g = generation;
                //调用await方法之前需要判断这一代是否已经坏掉了,如果坏掉了直接抛出异常
            if (g.broken)
                throw new BrokenBarrierException();
            //判断当前线程是否中断了,如果中断了也会抛出异常,并调用breakBarrier方法
            if (Thread.interrupted()) {
                breakBarrier();
                throw new InterruptedException();
            }
//如果这代没有坏掉,当前线程也没有中断,那么就将计数器减去1
            int index = --count;
            //减去1之后的数值如果为零,证明这一代已经满了,然后唤醒屏障拦截的所有线程
            if (index == 0) {  // tripped
                boolean ranAction = false;
                try {
                    //获取初始化的那个线程或者传入的线程
                    final Runnable command = barrierCommand;
                    if (command != null)
                    //线程执行任务
                        command.run();
                    ranAction = true;
                     //执行换代操作
                    nextGeneration();
                    return 0;
                } finally {
                     //如果上的操作执行出现问题了,那么就执行breakBarrier方法,唤醒所有线程
                    if (!ranAction)
                        breakBarrier();
                }
            }

            // loop until tripped, broken, interrupted, or timed out
            //自旋
            for (;;) {
                try {
                     //await()方法可以传入两个参数的,一个是否需要判断等待时间,一个等待的时间
                    //如果不需要判断等待时间,那么直接调用await()方法,这是Condition类的方法
                    if (!timed)
                        trip.await();
                        //如果需要等待的时间大于0,那么线程阻塞要设置时间,超过这个时间需要被唤醒
                    else if (nanos > 0L)
                     //设置超时阻塞时间,线程就被阻塞了,程序执行到这里就不会继续向下执行,直到这个线程被唤醒,才会继续向下执行
                        nanos = trip.awaitNanos(nanos);
                } catch (InterruptedException ie) {
                    if (g == generation && ! g.broken) {
                         //当前线程在阻塞过程,被中断了或者由于这一代屏障前的其他线程原因导致的代损坏了
                        //执行breakBarrier方法唤醒其他线程
                        breakBarrier();
                        throw ie;
                    } else {
                        // 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();
                    }
                }
                // 当有任何一个线程中断,会调用 breakBarrier 方法.
                // 就会唤醒其他的线程,其他线程醒来后,也要抛出异常
                //走到这里这名线程被唤醒了,唤醒的方式可能是计数器为零被唤醒,也有可能调用breakBarrier
                //方法导致代坏掉了而唤醒的,那么需要需要抛出代 破损异常
                if (g.broken)
                    throw new BrokenBarrierException();
                // g != generation 正常换代了,因为计数器为零了,代更新了
                //一切正常,返回当前线程所在屏障的下标
                // 如果 g == generation,说明还没有换代,那为什么会醒了?这里就是代的作用
                // 因为一个线程可以使用多个屏障,当别的屏障唤醒了这个线程,就会走到这里,所以需要判断是否是当前代。
                // 正是因为这个原因,才需要 generation 来保证正确。  
                if (g != generation)
                    return index;
            //如果不需要设置等待时间并且传入的时间不合法小于0,那么执行breakBarrier抛出异常
                if (timed && nanos <= 0L) {
                    breakBarrier();
                    throw new TimeoutException();
                }
            }
        } finally {
            lock.unlock();
        }
    }

    /**
     * Creates a new {@code CyclicBarrier} that will trip when the
     * given number of parties (threads) are waiting upon it, and which
     * will execute the given barrier action when the barrier is tripped,
     * performed by the last thread entering the barrier.
     *
     * @param parties the number of threads that must invoke {@link #await}
     *        before the barrier is tripped
     * @param barrierAction the command to execute when the barrier is
     *        tripped, or {@code null} if there is no action
     * @throws IllegalArgumentException if {@code parties} is less than 1
     */
    public CyclicBarrier(int parties, Runnable barrierAction) {
        if (parties <= 0) throw new IllegalArgumentException();
        this.parties = parties;
        this.count = parties;
        this.barrierCommand = barrierAction;
    }

    /**
     * Creates a new {@code CyclicBarrier} that will trip when the
     * given number of parties (threads) are waiting upon it, and
     * does not perform a predefined action when the barrier is tripped.
     *
     * @param parties the number of threads that must invoke {@link #await}
     *        before the barrier is tripped
     * @throws IllegalArgumentException if {@code parties} is less than 1
     */
    public CyclicBarrier(int parties) {
        this(parties, null);
    }

    /**
     * Returns the number of parties required to trip this barrier.
     *
     * @return the number of parties required to trip this barrier
     */
    public int getParties() {
        return parties;
    }

    /**
     * Waits until all {@linkplain #getParties parties} have invoked
     * {@code await} on this barrier.
     *
     * <p>If the current thread is not the last to arrive then it is
     * disabled for thread scheduling purposes and lies dormant until
     * one of the following things happens:
     * <ul>
     * <li>The last thread arrives; or
     * <li>Some other thread {@linkplain Thread#interrupt interrupts}
     * the current thread; or
     * <li>Some other thread {@linkplain Thread#interrupt interrupts}
     * one of the other waiting threads; or
     * <li>Some other thread times out while waiting for barrier; or
     * <li>Some other thread invokes {@link #reset} on this barrier.
     * </ul>
     *
     * <p>If the current thread:
     * <ul>
     * <li>has its interrupted status set on entry to this method; or
     * <li>is {@linkplain Thread#interrupt interrupted} while waiting
     * </ul>
     * then {@link InterruptedException} is thrown and the current thread's
     * interrupted status is cleared.
     *
     * <p>If the barrier is {@link #reset} while any thread is waiting,
     * or if the barrier {@linkplain #isBroken is broken} when
     * {@code await} is invoked, or while any thread is waiting, then
     * {@link BrokenBarrierException} is thrown.
     *
     * <p>If any thread is {@linkplain Thread#interrupt interrupted} while waiting,
     * then all other waiting threads will throw
     * {@link BrokenBarrierException} and the barrier is placed in the broken
     * state.
     *
     * <p>If the current thread is the last thread to arrive, and a
     * non-null barrier action was supplied in the constructor, then the
     * current thread runs the action before allowing the other threads to
     * continue.
     * If an exception occurs during the barrier action then that exception
     * will be propagated in the current thread and the barrier is placed in
     * the broken state.
     *
     * @return the arrival index of the current thread, where index
     *         {@code getParties() - 1} indicates the first
     *         to arrive and zero indicates the last to arrive
     * @throws InterruptedException if the current thread was interrupted
     *         while waiting
     * @throws BrokenBarrierException if <em>another</em> thread was
     *         interrupted or timed out while the current thread was
     *         waiting, or the barrier was reset, or the barrier was
     *         broken when {@code await} was called, or the barrier
     *         action (if present) failed due to an exception
     */
    public int await() throws InterruptedException, BrokenBarrierException {
        try {
            return dowait(false, 0L);
        } catch (TimeoutException toe) {
            throw new Error(toe); // cannot happen
        }
    }

    /**
     * Waits until all {@linkplain #getParties parties} have invoked
     * {@code await} on this barrier, or the specified waiting time elapses.
     *
     * <p>If the current thread is not the last to arrive then it is
     * disabled for thread scheduling purposes and lies dormant until
     * one of the following things happens:
     * <ul>
     * <li>The last thread arrives; or
     * <li>The specified timeout elapses; or
     * <li>Some other thread {@linkplain Thread#interrupt interrupts}
     * the current thread; or
     * <li>Some other thread {@linkplain Thread#interrupt interrupts}
     * one of the other waiting threads; or
     * <li>Some other thread times out while waiting for barrier; or
     * <li>Some other thread invokes {@link #reset} on this barrier.
     * </ul>
     *
     * <p>If the current thread:
     * <ul>
     * <li>has its interrupted status set on entry to this method; or
     * <li>is {@linkplain Thread#interrupt interrupted} while waiting
     * </ul>
     * then {@link InterruptedException} is thrown and the current thread's
     * interrupted status is cleared.
     *
     * <p>If the specified waiting time elapses then {@link TimeoutException}
     * is thrown. If the time is less than or equal to zero, the
     * method will not wait at all.
     *
     * <p>If the barrier is {@link #reset} while any thread is waiting,
     * or if the barrier {@linkplain #isBroken is broken} when
     * {@code await} is invoked, or while any thread is waiting, then
     * {@link BrokenBarrierException} is thrown.
     *
     * <p>If any thread is {@linkplain Thread#interrupt interrupted} while
     * waiting, then all other waiting threads will throw {@link
     * BrokenBarrierException} and the barrier is placed in the broken
     * state.
     *
     * <p>If the current thread is the last thread to arrive, and a
     * non-null barrier action was supplied in the constructor, then the
     * current thread runs the action before allowing the other threads to
     * continue.
     * If an exception occurs during the barrier action then that exception
     * will be propagated in the current thread and the barrier is placed in
     * the broken state.
     *
     * @param timeout the time to wait for the barrier
     * @param unit the time unit of the timeout parameter
     * @return the arrival index of the current thread, where index
     *         {@code getParties() - 1} indicates the first
     *         to arrive and zero indicates the last to arrive
     * @throws InterruptedException if the current thread was interrupted
     *         while waiting
     * @throws TimeoutException if the specified timeout elapses.
     *         In this case the barrier will be broken.
     * @throws BrokenBarrierException if <em>another</em> thread was
     *         interrupted or timed out while the current thread was
     *         waiting, or the barrier was reset, or the barrier was broken
     *         when {@code await} was called, or the barrier action (if
     *         present) failed due to an exception
     */
    public int await(long timeout, TimeUnit unit)
        throws InterruptedException,
               BrokenBarrierException,
               TimeoutException {
        return dowait(true, unit.toNanos(timeout));
    }

    /**
     * Queries if this barrier is in a broken state.
     *
     * @return {@code true} if one or more parties broke out of this
     *         barrier due to interruption or timeout since
     *         construction or the last reset, or a barrier action
     *         failed due to an exception; {@code false} otherwise.
     */
    public boolean isBroken() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            return generation.broken;
        } finally {
            lock.unlock();
        }
    }

    /**
     * Resets the barrier to its initial state.  If any parties are
     * currently waiting at the barrier, they will return with a
     * {@link BrokenBarrierException}. Note that resets <em>after</em>
     * a breakage has occurred for other reasons can be complicated to
     * carry out; threads need to re-synchronize in some other way,
     * and choose one to perform the reset.  It may be preferable to
     * instead create a new barrier for subsequent use.
     重置整个屏障为初始状态,将已经拦截的释放掉,全部重新开始计数
     */
    public void reset() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            //执行breakBarrier方法唤醒其他线程
            breakBarrier();   // break the current generation
            //更新屏障的状态并唤醒其他线程
            nextGeneration(); // start a new generation
        } finally {
            lock.unlock();
        }
    }

    /**
     * Returns the number of parties currently waiting at the barrier.
     * This method is primarily useful for debugging and assertions.
     *
     * @return the number of parties currently blocked in {@link #await}
     */
    public int getNumberWaiting() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            return parties - count;
        } finally {
            lock.unlock();
        }
    }
}

实例

假如公司团建,大家一起做大巴车,在大巴车出发之前,肯定是需要点名的,只有大家都到车上之后,才会发车,然后到了到了目的地之后,肯定是所有人都下车了,司机才能把车开走,这个过程中涉及了2次大家都就位之后,司机才能继续操作,可以证明CyclicBarrier可以循环使用计数器。

package com.runlion.middleground.marginal;

import lombok.Getter;
import lombok.Setter;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;

import java.util.Random;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.TimeUnit;

/**
 * 
 * @description:
 * @date 2024年04月30日 17:26:05
 */
@Slf4j
public class StudyTest {
    @Getter
    @Setter
    static class Flag{
        public int count=0;
    }
    @Test
    @SneakyThrows
    public void testCyclicBarrier() {
        Flag flag=new Flag();
        CyclicBarrier cyclicBarrier = new CyclicBarrier(5, () -> {
            if(flag.getCount()==0){
                System.out.println("所有人都上车了,可以发车了");
                flag.setCount(1);
            }else {
                System.out.println("所有人都下车了,司机可以走了");
            }

        });

        for (int i = 1; i < 6; i++) {
            new Thread(() -> {
                try {
                    System.out.println(Thread.currentThread().getName() + "号上车了");
                    cyclicBarrier.await();
                    System.out.println(Thread.currentThread().getName() + "号开始休息了");
                    TimeUnit.MILLISECONDS.sleep(new Random().nextInt(2000));
                    System.out.println(Thread.currentThread().getName() + "号下车了");
                    cyclicBarrier.await();
                    System.out.println(Thread.currentThread().getName() + "号到达目的地了");
                }catch (Exception e){
                    log.info(e.getMessage());
                }

            }, String.valueOf(i)).start();
        }
        System.out.println("主线程不阻塞");
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

当你老了:献给40岁以上还在求职的朋友

怪盗团团长按&#xff1a;本文作者是我的一位老朋友&#xff0c;他已经年过四十&#xff0c;在国内职场&#xff0c;算是不折不扣的中老年人了。难能可贵的是&#xff0c;最近他还换了工作&#xff0c;去了一个自己不熟悉的新行业奋斗。 我一直很纳闷&#xff0c;为何在中国&am…

该安装包不适配当前高性能处理器 请使用 64 位安装包

今天一台用户的一台手机报错&#xff0c;如下&#xff1a; 该安装包不适配当前高性能处理器 请使用 64 位安装包 查了下&#xff0c;网上也有人反馈该问题 https://ask.dcloud.net.cn/question/186865 最后在贴吧上发现答案&#xff1a;https://tieba.baidu.com/p/8773132859 …

Bluetooth Profile 蓝牙协议栈总结

GAP-Generic Access Profile 控制设备广播和连接 GAP profile 的目的是描述&#xff1a; Profile rolesDiscoverability modes and proceduresConnection modes and proceduresSecurity modes and procedures 设备连接过程 LE中GAP有4种角色&#xff1a;BroadcasterObserv…

使用groovy+spock优雅的进行单测

使用groovyspock优雅的进行单测 1. groovyspock示例1.1 简单示例1.2 增加where块的示例1.3 实际应用的示例 2. 单测相关问题2.1 与SpringBoot融合2.2 单测数据与测试数据隔离2.3 SQL自动转换&#xff08;MySQL -> H2&#xff09; 参考 Groovy是一种基于JVM的动态语言&#x…

LVGL自定义滑动

触摸和编码器都可以操作 typedef struct {lv_obj_t* obj;int16_t x;int16_t y;int16_t width;int16_t height; }pos_and_size_t;typedef struct {lv_obj_t* obj;lv_coord_t height;lv_coord_t width;lv_coord_t width_pad;lv_coord_t height_pad;lv_coord_t child_widget;lv_co…

2024王炸组合!基于Mamba的遥感图像处理引爆顶会!

对比传统方法&#xff0c;基于Mamba的遥感图像处理在计算效率和分析精度方面遥遥领先&#xff0c;Mamba遥感也成为了论文研究的新方向。 具体来说&#xff0c;在融合高分辨率的空间图像和低分辨率的光谱图像获取综合信息方面&#xff0c;Mamba可以提升性能&#xff0c;同时保持…

【Harmony3.1/4.0】笔记七-选项卡布局

概念 当页面信息较多时&#xff0c;为了让用户能够聚焦于当前显示的内容&#xff0c;需要对页面内容进行分类&#xff0c;提高页面空间利用率。Tabs组件可以在一个页面内快速实现视图内容的切换&#xff0c;一方面提升查找信息的效率&#xff0c;另一方面精简用户单次获取到的…

MySQL CRUD操作

前言&#x1f440;~ 上一章我们介绍了数据库的一些基础操作&#xff0c;关于如何去创建一个数据库&#xff0c;还有使用数据库&#xff0c;删 除数据库以及对表进行的一些基础操作&#xff0c;今天我们学习CRUD操作 俗称&#xff08;增删改查&#xff09; 如果各位对文章的内…

Objenesis 底层

Objenesis 简介 Objenesis 是一个 Java 库&#xff0c;用于在不调用构造方法的情况下创建对象。由于绕过了构造方法&#xff0c;所以无法调用构造方法中的初始化逻辑。相应的&#xff0c;Objenesis 无法创建抽象类、枚举、接口的实例对象。 起源 与其称之为起源&#xff0c;…

基于ST的STM32F407ZGT6嵌入式uCOS-III V3.08 操作系统工程实验

1.基于的开发板 2.原理图截图: 3.主控芯片框图与性能特点: High-performance foundation line, Arm Cortex-M4 core with DSP and FPU, 1 Mbyte of Flash memory, 168 MHz CPU, ART Accelerator, Ethernet, FSMC The STM32F405xx and STM32F407xx family is based on the high…

多家企业机密数据遭Lockbit3.0窃取,亚信安全发布《勒索家族和勒索事件监控报告》

本周态势快速感知 本周全球共监测到勒索事件87起&#xff0c;与上周相比勒索事件大幅下降。美国依旧为受勒索攻击最严重的国家&#xff0c;占比45%。 本周Cactus是影响最严重的勒索家族&#xff0c;Lockbit3.0和Bianlian恶意家族紧随其后&#xff0c;从整体上看Lockbit3.0依旧…

Meltdown 以及Linux KPTI技术简介

文章目录 前言一、Introduction二、 Background2.1 Out-of-order execution2.2 Address Spaces2.3 Cache Attacks 三、A Toy Example四、Building Blocks of the Attack4.1 Executing Transient Instructions4.2 Building a Covert Channel 五、Meltdown5.1 Attack Description…

深度学习之视觉特征提取器——LeNet

LeNet 引入 LeNet是是由深度学习巨头Yann LeCun在1998年提出&#xff0c;可以算作多层卷积网络在图像识别领域的首次成功应用。我们现在通常说的LeNet是指LeNet-5&#xff0c;最早的LeNet-1在1988年即开始研究&#xff0c;前后持续十年之久。但是&#xff0c;受限于当时计算机…

c++初阶——类和对象(下)

大家好&#xff0c;我是小锋&#xff0c;今天我们来学习我们类和对象的最后一个章节&#xff0c;我们本期的内容主要是类和对象的一些细节进行讲解 再谈构造函数 我们在初始化时有两种方式一种是函数体内初始化&#xff0c;一种是初始化列表 我们先来看看日期类的初始化 构造…

[机缘参悟-166] :周期论:万物的周期现象是这个世界有序性和稳定性保障;超越周期:在轮回中,把握周期节奏。

目录 前言&#xff1a;超越周期 一、周期是大自然和宇宙的规律&#xff0c;是天道 1.1 概述 1.2 万物的周期规律的现象 1.3 电磁波的周期 二、计算机世界中的周期性 三、佛家的生命轮回规律 四、人类社会发展的周期规律 五、经济活动的周期规律 5.1 概述 5.2 股市的…

Ieetcode——21.合并两个有序链表

21. 合并两个有序链表 - 力扣&#xff08;LeetCode&#xff09; 合并两个有序链表我们的思路是创建一个新链表&#xff0c;然后遍历已知的两个有序链表&#xff0c;并比较其节点的val值&#xff0c;将小的尾插到新链表中&#xff0c;然后继续遍历&#xff0c;直到将该两个链表…

C语言实验-函数与模块化程序设计

一&#xff1a; 编写函数fun&#xff0c;其功能是&#xff1a;输入一个正整数&#xff0c;将其每一位上为偶数的数取出重新构成一个新数并输出。主函数负责输入输出&#xff0c;如输入87653142&#xff0c;则输出8642。&#xff08;main函数->fun函数&#xff09; #define _…

【代码问题】【Pytorch】训练模型时Loss为NaN或INF

解决方法或者问题排查&#xff1a; 加归一化层&#xff1a; 我的问题是我新增的一个模块与原来的模块得到的张量相加&#xff0c;原张量是归一化后的&#xff0c;我的没有&#xff1a; class Module(nn.Module):def __init__(self,dim,):super().__init__()# 新增一个LayerNo…

节假日如何快速回应客户消息?

在宝贵的休闲时光或者特殊的节日期间&#xff0c;有时候由于工作、家庭等原因&#xff0c;我们很难及时回应客户的消息。那么如何在忙碌之时&#xff0c;如何确保与他人的交流畅通无阻呢&#xff1f;答案就是使用微信私域流量管理系统。 01 机器人自动回复设置 机器人自动回…

酷我音乐车机版+v6.0.1.0车机共存会员版【附带安装包下载地址】

简介 很多车机的酷我音乐app有限制&#xff0c;不能完全使用酷我音乐的所有功能。我这里分享一个可以使用全部功能的酷我音乐app&#xff0c;大家可以自行下载。 界面预览 软件下载地址【转存到自己的网盘后即可下载】 网盘地址&#xff1a;https://pan.xunlei.com/s/VNwgzNV…
最新文章