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

React18 setState是同步还是异步?

相信大家对于react的setState肯定是不陌生了, 这是一个用于更新状态的函数. 但是在之前有一道非常经典的面试题就是关于setState是同步还是异步的问题, 具体可以参考我之前写的一篇文章: 一篇文章彻底理解setState是同步还是异步!. 对于react 18之前的版本, 上文说的东西确实没错, 但是react团队已经在18中对批处理的行为做了更改, 会尽可能的将所有能进行批处理的内容都进行批处理, 以获取更好的性能, 今天我们就来聊聊react 18中对这一行为做了哪些更改.

先看现象, 再说结论

我们都用下面这段代码在不同版本的react上进行测试

class App extends React.Component {state = {data: 1}test = () => {setTimeout(() => {this.setState({data: 2});console.log('data', this.state.data);this.setState({data: 3});console.log('data', this.state.data);}, 0);}render() {console.log("render");return (<div><button onClick={this.test}>{this.state.data}</button></div>)}
}

大家觉得输出的data值会是什么, 这个render又会打印几次?

在react 17.x下, 我们能够同步的获取到data的值, 所以输出会是2, 3.同时也会经历两次react的整个render过程, 所以也会导致render被打印两次, 这都是因为setTimeout带来的影响, 具体的解释可以参考之前的文章.

而在最新版的react 18上, 这两个setData也会被异步的批处理, 合并为一次进行更新, 所以我们拿到的值始终是1, render函数也只会被打印一次.

react 18做了什么修改

官方的说明在这里: https://github.com/reactwg/react-18/discussions/21

总结一下就是:

从react 18开始, 使用了createRoot创建应用后, 所有的更新都会自动进行批处理(也就是异步合并).使用render的应用会保持之前的行为.

如果你想保持同步更新行为, 可以使用ReactDOM.flushSync().

// 新版
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
const container = document.getElementById('root');
// Create a root.
const root = ReactDOM.createRoot(container);
// Render the top component to the root.
root.render(<App />);// 旧版
// import React from 'react';
// import ReactDOM from 'react-dom';
// import './index.css';
// import App from './App';
// import reportWebVitals from './reportWebVitals';// ReactDOM.render(
//   <React.StrictMode>
//     <App />
//   </React.StrictMode>,
//   document.getElementById('root')
// );// // If you want to start measuring performance in your app, pass a function
// // to log results (for example: reportWebVitals(console.log))
// // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
// reportWebVitals();

至于为什么会这样, 我们直接从源码入手就好了.

我们知道, react的setState最终会走到scheduleUpdateOnFiber来进行更新, 之前最关键的一段代码就在这里

// react 17.x
if (executionContext === NoContext) {// Flush the synchronous work now, unless we're already working or inside// a batch. This is intentionally inside scheduleUpdateOnFiber instead of// scheduleCallbackForFiber to preserve the ability to schedule a callback// without immediately flushing it. We only do this for user-initiated// updates, to preserve historical behavior of legacy mode.resetRenderTimer();flushSyncCallbackQueue();
}

executionContext代表了react当前的调度状态, 如果退出了react的调度这个值就会重新变成NoContext. 也就是说, 如果你调用setState的时候并不处于react的调度状态中, 那么就会同步的去执行你的setState.这也是为什么一旦我们使用一些异步操作就会导致setState变成同步的原因, 而在react 18中这段代码变成了这样

// react 18.x
if (lane === SyncLane && executionContext === NoContext && (fiber.mode & ConcurrentMode) === NoMode && // Treat `act` as if it's inside `batchedUpdates`, even in legacy mode.!( ReactCurrentActQueue$1.isBatchingLegacy)) {// Flush the synchronous work now, unless we're already working or inside// a batch. This is intentionally inside scheduleUpdateOnFiber instead of// scheduleCallbackForFiber to preserve the ability to schedule a callback// without immediately flushing it. We only do this for user-initiated// updates, to preserve historical behavior of legacy mode.resetRenderTimer();flushSyncCallbacksOnlyInLegacyMode();
}

可以看到我们多出了好几个判断条件, 除了之前的 executionContext === NoContext 之外, 还多了三个判断条件, 我们一个一个来看看.

lane === SyncLane

这个其实没什么好说的, lane是react中和优先级有关的概念, 从函数命名就可以看出, 只有是同步任务才会进到这个if.

var TotalLanes = 31;
var NoLanes =
/*                        */
0;
var NoLane =
/*                          */
0;
var SyncLane =
/*                        */
1;
var InputContinuousHydrationLane =
/*    */
2;
var InputContinuousLane =
/*            */
4;
var DefaultHydrationLane =
/*            */
8;
var DefaultLane =
/*                    */
16;
var TransitionHydrationLane =
/*                */
32;
var TransitionLanes =
/*                       */
4194240;
var TransitionLane1 =
/*                        */
64;
var TransitionLane2 =
/*                        */
128;
var TransitionLane3 =
/*                        */
256;
var TransitionLane4 =
/*                        */
512;
var TransitionLane5 =
/*                        */
1024;
var TransitionLane6 =
/*                        */
2048;
var TransitionLane7 =
/*                        */
4096;
var TransitionLane8 =
/*                        */
8192;
var TransitionLane9 =
/*                        */
16384;
var TransitionLane10 =
/*                       */
32768;
var TransitionLane11 =
/*                       */
65536;
var TransitionLane12 =
/*                       */
131072;
var TransitionLane13 =
/*                       */
262144;
var TransitionLane14 =
/*                       */
524288;
var TransitionLane15 =
/*                       */
1048576;
var TransitionLane16 =
/*                       */
2097152;
var RetryLanes =
/*                            */
130023424;
var RetryLane1 =
/*                             */
4194304;
var RetryLane2 =
/*                             */
8388608;
var RetryLane3 =
/*                             */
16777216;
var RetryLane4 =
/*                             */
33554432;
var RetryLane5 =
/*                             */
67108864;
var SomeRetryLane = RetryLane1;
var SelectiveHydrationLane =
/*          */
134217728;
var NonIdleLanes =
/*                                 */
268435455;
var IdleHydrationLane =
/*               */
268435456;
var IdleLane =
/*                       */
536870912;
var OffscreenLane =
/*                   */
1073741824; // This function is used for the experimental timeline (react-devtools-timeline)
// It should be kept in sync with the Lanes values above.

这一堆东西就代表了react内部各种不同优先级的任务.

(fiber.mode & ConcurrentMode) === NoMode

react fiber的mode属性代表了不同的渲染模式

var NoMode =
/*                         */
0; // TODO: Remove ConcurrentMode by reading from the root tag insteadvar ConcurrentMode =
/*                 */
1;
var ProfileMode =
/*                    */
2;
var DebugTracingMode =
/*               */
4;
var StrictLegacyMode =
/*               */
8;
var StrictEffectsMode =
/*              */
16;

具体是什么意思呢, 比如说ProfileMode代表了性能调试模式, 我们的开发环境的mode就会被赋予这个值, 可以在控制台中给开发者输出一些提示信息.而其他的值是啥意思呢..其实我目前也不能很明白的给大家说清楚, 因为我也没研究过, 但是只要知道这是个区分不同模式的变量就行了.

而这个值会在什么地方赋给fiber的mode属性呢, 会在我们整个应用的入口, 然后经过一系列的函数调用, 最终会走到createHostRootFiber方法

function createHostRootFiber(tag, isStrictMode, concurrentUpdatesByDefaultOverride) {var mode;if (tag === ConcurrentRoot) {mode = ConcurrentMode;if (isStrictMode === true) {mode |= StrictLegacyMode;{mode |= StrictEffectsMode;}}} else {mode = NoMode;}if ( isDevToolsPresent) {// Always collect profile timings when DevTools are present.// This enables DevTools to start capturing timing at any point–// Without some nodes in the tree having empty base times.mode |= ProfileMode;}return createFiber(HostRoot, null, null, mode);
}

其中tag的值就是根据入口函数一路传下来的. 我们之前提到, 要想体验自动批处理需要在应用入口将ReactDOM.render替换为ReactDOM.createRoot, 这两者有什么区别呢:

function render(element, container, callback) {// 省略...return legacyRenderSubtreeIntoContainer(null, element, container, false, callback);
}function legacyRenderSubtreeIntoContainer(parentComponent, children, container, forceHydrate, callback) {// 省略...if (!root) {// Initial mountroot = container._reactRootContainer = legacyCreateRootFromDOMContainer(container, forceHydrate);fiberRoot = root;// 省略...} else {// 省略...}return getPublicRootInstance(fiberRoot);
}function legacyCreateRootFromDOMContainer(container, forceHydrate) {// 省略...var root = createContainer(container, LegacyRoot, forceHydrate, null, // hydrationCallbacksfalse, // isStrictModefalse, // concurrentUpdatesByDefaultOverride,'' // identiferPrefix);// 省略...return root;
}function createContainer(containerInfo, tag, hydrate, hydrationCallbacks, isStrictMode, concurrentUpdatesByDefaultOverride, identifierPrefix) {return createFiberRoot(containerInfo, tag, hydrate, hydrationCallbacks, isStrictMode, concurrentUpdatesByDefaultOverride, identifierPrefix);
}function createFiberRoot(containerInfo, tag, hydrate, hydrationCallbacks, isStrictMode, concurrentUpdatesByDefaultOverride, identifierPrefix) {// 省略...var uninitializedFiber = createHostRootFiber(tag, isStrictMode);root.current = uninitializedFiber;uninitializedFiber.stateNode = root;// 省略...return root;
}function createHostRootFiber(tag, isStrictMode, concurrentUpdatesByDefaultOverride) {var mode;if (tag === ConcurrentRoot) {mode = ConcurrentMode;if (isStrictMode === true) {mode |= StrictLegacyMode;{mode |= StrictEffectsMode;}}} else {mode = NoMode;}if ( isDevToolsPresent) {// Always collect profile timings when DevTools are present.// This enables DevTools to start capturing timing at any point–// Without some nodes in the tree having empty base times.mode |= ProfileMode;}return createFiber(HostRoot, null, null, mode);
}

我这里把从ReactDOM.render到createHostRootFiber的全部函数都放在了这里, 可以看到最终createHostRootFiber拿到的tag值其实是LegacyRoot, 所以mode最终会等于NoMode. 随后如果在开发环境下还会被赋予一个ProfileMode, 也就是我们之前说的调试模式.

所以即使我们升级到了18.x的版本, 但是如果仍然使用ReactDOM.render来创建我们的应用, 我们就会在这个条件得到false

(fiber.mode & ConcurrentMode) === NoMode

因为我们的fiber.mode并没有被赋值为ConcurrentMode.而当我们使用ReactDOM.createRoot来创建应用时, 也是一路跟着函数找下去, 会发现tag最后拿到的是ConcurrentRoot, 也就是说mode会被赋值为ConcurrentMode, 所以也就是为什么只有使用了ReactDOM.createRoot的应用才会有该特性的原因.

ps: mode是可以被赋值为多个值的, 区分这多个值是通过&操作和|操作, 因为mode其实是一个二进制值, 不理解这块的同学可以再想想二进制值的&、|操作.

!( ReactCurrentActQueue$1.isBatchingLegacy)

其实这个条件注释已经写的很清楚了

// Treat `act` as if it's inside `batchedUpdates`, even in legacy mode.

就是在act函数调用的时候也进行同步更新, act是个啥玩意呢.我们直接抄一下官网的介绍:

在编写UI测试时,可以将渲染、用户事件或数据获取等任务视为与用户界面交互的“单元”。react-dom/test-utils提供了一个名为act()的 helper, 它确保在进行任何断言之前, 与这些“单元”相关的所有更新都已处理并应用于DOM:

act(() => {// 渲染组件
});
// 进行断言

这有助于使测试运行更接近真实用户在使用应用程序时的体验。这些示例的其余部分使用act()来作出这些保证。

而isBatchingLegacy也正是在act函数中被赋值为true

function act(callback) {{// `act` calls can be nested, so we track the depth. This represents the// number of `act` scopes on the stack.var prevActScopeDepth = actScopeDepth;actScopeDepth++;if (ReactCurrentActQueue.current === null) {// This is the outermost `act` scope. Initialize the queue. The reconciler// will detect the queue and use it instead of Scheduler.ReactCurrentActQueue.current = [];}var prevIsBatchingLegacy = ReactCurrentActQueue.isBatchingLegacy;var result;try {// Used to reproduce behavior of `batchedUpdates` in legacy mode. Only// set to `true` while the given callback is executed, not for updates// triggered during an async event, because this is how the legacy// implementation of `act` behaved.ReactCurrentActQueue.isBatchingLegacy = true;result = callback(); // Replicate behavior of original `act` implementation in legacy mode,// which flushed updates immediately after the scope function exits, even// if it's an async function.if (!prevIsBatchingLegacy && ReactCurrentActQueue.didScheduleLegacyUpdate) {var queue = ReactCurrentActQueue.current;if (queue !== null) {ReactCurrentActQueue.didScheduleLegacyUpdate = false;flushActQueue(queue);}}} catch (error) {popActScope(prevActScopeDepth);throw error;} finally {ReactCurrentActQueue.isBatchingLegacy = prevIsBatchingLegacy;}//...省略}
}

综上所述

所以, 在升级到18版本之后的react只有在你使用ReactDOM.render的时候(LegacyMode)才会保持之前的行为, 否则都会对你的更新进行合并处理, 也就是自动批处理. 从我们最后调用的函数名也能看出这一点: flushSyncCallbacksOnlyInLegacyMode

被遗忘的 flushSync

我们之前还提到, 如果我们想进行同步更新可以使用flushSync函数, 那么它又干了啥.

function flushSync(fn) {// In legacy mode, we flush pending passive effects at the beginning of the// next event, not at the end of the previous one.if (rootWithPendingPassiveEffects !== null && rootWithPendingPassiveEffects.tag === LegacyRoot && (executionContext & (RenderContext | CommitContext)) === NoContext) {flushPassiveEffects();}var prevExecutionContext = executionContext;executionContext |= BatchedContext;var prevTransition = ReactCurrentBatchConfig$3.transition;var previousPriority = getCurrentUpdatePriority();try {ReactCurrentBatchConfig$3.transition = 0;setCurrentUpdatePriority(DiscreteEventPriority);if (fn) {return fn();} else {return undefined;}} finally {setCurrentUpdatePriority(previousPriority);ReactCurrentBatchConfig$3.transition = prevTransition;executionContext = prevExecutionContext; // Flush the immediate callbacks that were scheduled during this batch.// Note that this will happen even if batchedUpdates is higher up// the stack.if ((executionContext & (RenderContext | CommitContext)) === NoContext) {flushSyncCallbacks();}}
}

可以看到, 这个函数会在执行完传给他的fn函数后马上去清空一次更新队列, 也就是调用flushSyncCallbacks方法, 就是我们之前在异步中调用setState的行为.

值得一提的是, 如果我们在react 18中想达到之前的效果, 这样写是不行的:

import React from 'react';
import { flushSync } from 'react-dom';class App extends React.Component {state = {data: 1}test = () => {setTimeout(() => {flushSync(() => {this.setState({data: 2});console.log('data', this.state.data);this.setState({data: 3});console.log('data', this.state.data);});}, 0);}render() {console.log("render");return (<div><button onClick={this.test}>{this.state.data}</button></div>)}
}export default App;

这样写两个setState还是会被合并为同一个, 因为调用完setState之后并不会马上去刷新更新队列, 只有在整个函数执行完以后才会对队列进行刷新. 所以如果两个setState都写在一个flushSync里面是没有效果的.要想达到之前的效果需要这样写:

import React from 'react';
import { flushSync } from 'react-dom';class App extends React.Component {state = {data: 1}test = () => {setTimeout(() => {flushSync(() => {this.setState({data: 2});});console.log('data', this.state.data);flushSync(() => {this.setState({data: 3});});console.log('data', this.state.data);}, 0);}render() {console.log("render");return (<div><button onClick={this.test}>{this.state.data}</button></div>)}
}

ps: 我们一直说的同步异步并不是指setState本身, setState本身一直一个同步函数, 我们指的是调用完setState后react会同步的去执行后续的步骤还是会异步的去执行后续的步骤.

结语

react官方做出这个改变其实也是为了更好的性能去考虑的, 毕竟调用完setState之后同步的进行渲染有时候会导致很多没必要的开销, 特别是在进行数据请求时, 很容易写出多个同步的setState.

而随着这波更新, 可能以后这道经典的面试题也会随之消散, 毕竟往后不论在什么情况下, 默认行为都会帮你对setState进行合并更新, 不再会进行同步处理了.

相关文章:

React18 setState是同步还是异步?

相信大家对于react的setState肯定是不陌生了, 这是一个用于更新状态的函数. 但是在之前有一道非常经典的面试题就是关于setState是同步还是异步的问题, 具体可以参考我之前写的一篇文章: 一篇文章彻底理解setState是同步还是异步&#xff01;. 对于react 18之前的版本, 上文说的…...

Kafka消费者 TCP管理

Kafka消费者 TCP管理创建 TCPFindCoordinator连接协调者消费数据TCP 连接数关闭 TCP 连接消费者的程序入口类是 KafkaConsumer 构建 KafkaConsumer 时 &#xff0c;不会创建任何 TCP 连接TCP 连接是用 KafkaConsumer.poll 创建 创建 TCP poll 创建 TCP 的地方 : 发起 FindC…...

软考高级备考哪一个类型好些?

软考高级是比中级和初级难&#xff0c;科目就要考三科&#xff0c;选择题基础知识简答题案例分析写作论文 软考高级科目有&#xff1a;信息系统项目管理师、系统分析师、系统架构设计师、网络规划师、系统规划与管理师。如下&#xff1a; 软考高级中高项信息系统项目管理师师比…...

2023 HBU 天梯赛第一次测试 题目集

目录 1 建校日期 2 发射小球 3 背上书包去旅行 4 吉利的数字 5 向前走 6 热水器 7 走方格 8 朋友圈 9 交保护费 10 走方格 11 和与积 12 缩短字符串 13 买木棒 1 建校日期 在2022 ICPC沈阳站上&#xff0c;东北大学命题组给参赛的选手们出了一道签到题&#xff0…...

华为OD机试题,用 Java 解【子序列长度】问题

华为Od必看系列 华为OD机试 全流程解析+经验分享,题型分享,防作弊指南)华为od机试,独家整理 已参加机试人员的实战技巧华为od 2023 | 什么是华为od,od 薪资待遇,od机试题清单华为OD机试真题大全,用 Python 解华为机试题 | 机试宝典使用说明 参加华为od机试,一定要注意不…...

内网环境解决SSL证书问题

本来这个没什么好写的&#xff0c;但是坑实在有点多&#xff0c;不得不写个文章记录下来。 创建证书看这里&#xff01;&#xff01;&#xff01; 很多知识点要结合这个页面内容来看。 创建证书已经看过相关文章&#xff0c;然后用unity跑的时候发现连不上&#xff0c;完全没…...

数据分析方法01对比分析法

对比分析法 1、概念 基于相同的数据标准下&#xff0c;把两个及以上相互联系的指标数据进行比较&#xff0c;准确量化的分析他们的差异&#xff0c;说明研究对象在规模大小&#xff0c;水平高低&#xff0c;速度快慢等的不同表现&#xff0c;目的是为了找到差异的原因&#x…...

基于SMOKE多模式排放清单处理技术及EDGAR/MEIC清单制作与VOCs排放量核算

查看原文>>>基于SMOKE多模式排放清单处理技术及EDGAR/MEIC清单制作与VOCs排放量核算 (qq.com)随着我国经济快速发展&#xff0c;我国面临着日益严重的大气污染问题。近年来&#xff0c;严重的大气污染问题已经明显影响国计民生&#xff0c;引起政府、学界和人们越来越…...

CSS流动布局-页面自适应

项目中经常会碰到页面自适应的问题&#xff0c;例如&#xff1a;商城的列表展示、分类列表展示等页面&#xff0c;如下&#xff1a; 该页面会随着页面的放大缩小而随之发生变化&#xff0c;这种自适应的页面布局在大屏幕、小屏幕、不同的浏览器设备上都应该呈现出与设计匹配的…...

3.Elasticsearch初步进阶

3.Elasticsearch初步进阶[toc]1.文档批量操作批量获取文档数据批量获取文档数据是通过_mget的API来实现的在URL中不指定index和type请求方式:GET请求地址:_mget功能说明:可以通过ID批量获取不同index和type的数据请求参数docs:文档数组参数_index:指定index_type:指定type_id:指…...

优思学院|六西格玛管理的核心理念是什么?

六西格玛管理是一种基于数据分析的质量管理方法&#xff0c;旨在通过降低过程的变异性来达到质量稳定和优化的目的。该方法以希腊字母“σ”为名&#xff0c;代表标准差&#xff0c;是衡量过程变异性的重要指标。 六西格玛管理的核心理念是“以客户为中心、以数据为基础、追求…...

第十七节 多态

多态 什么是多态? ●同类型的对象&#xff0c;执行同一个行为&#xff0c;会表现出不同的行为特征。 多态的常见形式 父类类型 对象名称new子类构造器; 接口 对象名称new 实现类构造器; 多态中成员访问特点 ●方法调用:编译看左边&#xff0c;运行看右边。 ●变量调用:编译看…...

[vue]提供一种网站底部备案号样式代码

演示 vue组件型&#xff08;可直接用&#xff09; 组件代码&#xff1a;copyright-icp.vue <template><div class"icp">{{© ${year} ${author} }}<a href"http://beian.miit.gov.cn/" target"_blank">{{ record }}</a…...

python第四天作业~函数练习

目录 作业4、判断以下哪些不能作为标识符 A、a B、&#xffe5;a C、_12 D、$a12 E、false F、False 作业5&#xff1a; 输入数&#xff0c;判断这个数是否是质数&#xff08;要求使用函数 for循环&#xff09; 作业6&#xff1a;求50~150之间的质数是…...

linux安装influxdb-rpmyum方式

一、influxdb的安装InfluxDB简介时序数据库InfluxDB版是一款专门处理高写入和查询负载的时序数据库&#xff0c;用于存储大规模的时序数据并进行实时分析&#xff0c;包括来自DevOps监控、应用指标和IoT传感器上的数据主要特点&#xff1a;专为时间序列数据量身订造高性能数据存…...

死锁

1.死锁的定义 多线程以及多进程改善了系统资源的利用率并提高了系统 的处理能力。然而&#xff0c;并发执行也带来了新的问题——死锁。所谓死锁是指多个线程因竞争资源而造成的一种僵局&#xff08;互相等待&#xff09;&#xff0c;若无外力作用&#xff0c;这些进程都将无法…...

C++基础了解-05-C++常量

C常量 一、C常量 常量是固定值&#xff0c;在程序执行期间不会改变。这些固定的值&#xff0c;又叫做字面量。 常量可以是任何的基本数据类型&#xff0c;可分为整型数字、浮点数字、字符、字符串和布尔值。 常量就像是常规的变量&#xff0c;只不过常量的值在定义后不能进…...

深度学习笔记-2.自动梯度问题

通过反向传播进行自动求梯度1-requires_grad问题2-梯度3- detach() 和 with torch.no_grad(&#xff09;4- Tensor.data.requires_gradPyTorch提供的autograd包能够根据输入和前向传播过程自动构建计算图&#xff0c;并执行反向传播. 1-requires_grad问题 requires_gradTrue …...

一文读懂倒排序索引涉及的核心概念

基础概念相信对于第一次接触Elasticsearch的同学来说&#xff0c;最难理解的概念就是倒排序索引&#xff08;也叫反向索引&#xff09;&#xff0c;因为这个概念跟我们之前在传统关系型数据库中的索引概念是完全不同的&#xff01;在这里我就重点给大家介绍一下倒排序索引&…...

Java基础算法题

以创作之名致敬节日 胜固欣然&#xff0c;败亦可喜。 --苏轼 目录 练习1 : 优化代码 扩展 : CRTL Alt M 自动抽取方法 练习2: 方法一: 方法二: 方法三: Math : 顾名思义&#xff0c;Math类就是用来进行数学计算的&#xff0c;它提供了大量的静态方法来便于我们实…...

「SAP ABAP」你真的了解OPEN SQL的DML语句吗 (附超详细案例讲解)

&#x1f482;作者简介&#xff1a; THUNDER王&#xff0c;一名热爱财税和SAP ABAP编程以及热爱分享的博主。目前于江西师范大学本科在读&#xff0c;同时任汉硕云&#xff08;广东&#xff09;科技有限公司ABAP开发顾问。在学习工作中&#xff0c;我通常使用偏后端的开发语言A…...

数据结构3——线性表2:线性表的顺序结构

顺序结构的基本理解 定义&#xff1a; 把逻辑上相邻的数据元素存储在物理上相邻&#xff08;占用一片连续的存储单元&#xff0c;中间不能空出来&#xff09;的存储单元的存储结构 存储位置计算&#xff1a; LOC(a(i1))LOC(a(i))lLOC(a(i1))LOC(a(i))l LOC(a(i1))LOC(a(i))l L…...

VMware虚拟机搭建环境通用方法

目录一、前期准备1.下载并安装一个虚拟机软件二、开始创建虚拟机1.配置虚拟机硬件相关操作2.虚拟机网络相关操作三、开机配置相关内容0.开机遇到报错处理&#xff08;选看--开机没有报错请忽略&#xff09;1.开始配置2.开机之后配置3.使用xshell远程登录4.使用xshell配置虚拟机…...

2.Fully Convolutional Networks for Semantic Segmentation论文记录

欢迎访问个人网络日志&#x1f339;&#x1f339;知行空间&#x1f339;&#x1f339; 文章目录1.基础介绍2.分类网络转换成全卷积分割网络3.转置卷积进行上采样4.特征融合5.一个pytorch源码实现参考资料1.基础介绍 论文:Fully Convolutional Networks for Semantic Segmentati…...

深度解析Spring Boot自动装配原理

废话不多说了&#xff0c;直接来看源码。源码解析SpringBootApplication我们在使用idea创建好Spring Boot项目时&#xff0c;会发现在启动类上添加了SpringBootApplication注解&#xff0c;这个注解就是Spring Boot的核心所在。点击注解可以查看到到它的实现ementType.TYPE) Re…...

Redis性能分析相关-channel=[id: 0xbee27bd4, L:/127.0.0.1:63156

redis宕机...

Linux:环境变量

目录一、环境变量的理解&#xff08;1&#xff09;什么是环境变量&#xff1f;&#xff08;2&#xff09;Linux中的环境变量二、环境变量的使用&#xff08;1&#xff09;PATH环境变量&#xff08;2&#xff09;和变量相关的指令三、环境变量与普通变量的区别在平时使用电脑的时…...

Codeforces Round 703 (Div. 2)(A~D)

A. Shifting Stacks给出一个数组&#xff0c;每次可以将一个位置-1&#xff0c;右侧相邻位置1&#xff0c;判断是否可以经过若干次操作后使得数列严格递增。思路&#xff1a;对于每个位置&#xff0c;前缀和必须都大于该位置应该有的最少数字&#xff0c;即第一个位置最少是0&a…...

Django项目5——基于tensorflow serving部署深度模型——windows版本

1&#xff1a;安装docker for windows 可能需要安装WLS2&#xff0c;用于支持Linux系统&#xff0c;参照上面的教程安装 2&#xff1a;在Powershell下使用docker docker pull tensorflow/serving3&#xff1a;在Powershell下启动tensorflow serving docker run -p 8500:8500 …...

MySQL基础篇3

第一章 多表关系实战 1.1 实战1&#xff1a;省和市 方案1&#xff1a;多张表&#xff0c;一对多 方案2&#xff1a;一张表&#xff0c;自关联一对多 id1 name‘北京’ p_id null; id2 name‘昌平’ p_id1 id3 name‘大兴’ p_id1 id3 name‘上海’ p_idnull id4 name‘浦东’…...