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

【go从入门到精通】for和for range的区别

 作者简介:

        高科,先后在 IBM PlatformComputing从事网格计算,淘米网,网易从事游戏服务器开发,拥有丰富的C++,go等语言开发经验,mysql,mongo,redis等数据库,设计模式和网络库开发经验,对战棋类,回合制,moba类页游,手游有丰富的架构设计和开发经验。 (谢谢你的关注)
————————————————

for 和 for range有什么区别?

for可以遍历array和slice,遍历key为整型递增的map,遍历string

for range可以完成所有for可以做的事情,却能做到for不能做的,包括遍历key为string类型的map并同时获取key和value,遍历channel

所以除此之外还有其他区别吗?我们来用几个代码块说明他们的区别不仅仅是上面的这几点

测试代码

让我们用切片和数组对for range ifor range v for i循环进行一些测试:

package main_testimport "testing"var intsSlice = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100}
var intsArray = [...]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100}func BenchmarkForRangeI_Slice(b *testing.B) {sum := 0for n := 0; n < b.N; n++ {for i := range intsSlice {sum += intsSlice[i]}}
}func BenchmarkForRangeV_Slice(b *testing.B) {sum := 0for n := 0; n < b.N; n++ {for _, v := range intsSlice {sum += v}}
}func BenchmarkForI_Slice(b *testing.B) {sum := 0for n := 0; n < b.N; n++ {for i := 0; i < len(intsSlice); i++ {sum += intsSlice[i]}}
}func BenchmarkForRangeI_Array(b *testing.B) {sum := 0for n := 0; n < b.N; n++ {for i := range intsArray {sum += intsArray[i]}}
}func BenchmarkForRangeV_Array(b *testing.B) {sum := 0for n := 0; n < b.N; n++ {for _, v := range intsArray {sum += v}}
}func BenchmarkForI_Array(b *testing.B) {sum := 0for n := 0; n < b.N; n++ {for i := 0; i < len(intsArray); i++ {sum += intsArray[i]}}
}

运行结果如下:

go test -bench=. for_test.go -benchtime 100000000x
goos: windows
goarch: amd64
cpu: 11th Gen Intel(R) Core(TM) i5-11400H @ 2.70GHz
BenchmarkForRangeI_Slice-12 100000000 33.87 ns/op
BenchmarkForRangeV_Slice-12 100000000 33.91 ns/op
BenchmarkForI_Slice-12 100000000 40.68 ns/op
BenchmarkForRangeI_Array-12 100000000 28.47 ns/op
BenchmarkForRangeV_Array-12 100000000 28.57 ns/op
BenchmarkForI_Array-12 100000000 28.40 ns/op
PASS
ok command-line-arguments 19.439s 

正如我们所看到的,对于切片来说, for i循环比for range 循环要慢一些,但对于数组来说没有区别……但是为什么呢?

首先让我们看一下github.com上的切片结构:

type slice struct { 
array unsafe.Pointer    // 数组数据位于 (slice + 0) 地址len    int               // 数组长度位于 (slice + 8) 地址cap    int               // 数组容量位于 (slice + 16) 地址
}

反汇编

然后,让我们通过运行go tool objdump命令深入了解反汇编程序,并尝试找出 Go 编译器为我们做了什么:

for 循环遍历切片

sum := 0
for i := 0; i < len(intsSlice); i++ {sum += intsSlice[i]
}

反汇编:

0x48dd34 XORL AX, AX
0x48dd36 XORL CX, CX
0x48dd38 JMP 0x48dd48 # jump to the 5-th instruction of the loop
######################## loop start ##########################
0x48dd3a LEAQ 0x1(AX), BX # store AX (index counter) + 1 in BX
0x48dd3e MOVQ 0(DX)(AX*8), DX # store quadword (8 bytes) from DX (data pointer) + AX (index counter) * 8 address to DX
0x48dd42 ADDQ DX, CX # add DX value to CX (our sum accumulator)
0x48dd45 MOVQ BX, AX # set BX (previously incremented AX by 1) value to AX (index counter)
0x48dd48 MOVQ main.intsSlice(SB), DX # store slice data pointer in DX (from static address)
0x48dd4f CMPQ AX, main.intsSlice+8(SB) # compare to slice data size (static address)
0x48dd56 JG 0x48dd3a # jump back to start if slice size is greater than AX (index counter)
######################## loop end ########################## 

for range循环遍历slice

sum := 0 
for i := range intsSlice { sum += intsSlice[i] 
}

以及反汇编:

0x48dd34 MOVQ main.intsSlice(SB), CX # store slice data pointer in CX (from static address)
0x48dd3b MOVQ main.intsSlice+8(SB), DX # store slice data size in DX (from static address)
0x48dd42 XORL AX, AX
0x48dd44 XORL BX, BX
0x48dd46 JMP 0x48dd56 # jump to the 5-th instruction of the loop
######################## loop start ##########################
0x48dd48 LEAQ 0x1(AX), SI # store AX (index counter) + 1 in SI
0x48dd4c MOVQ 0(CX)(AX*8), DI # store quadword (8 bytes) from CX (data pointer) + AX (index counter) * 8 address to DI
0x48dd50 ADDQ DI, BX # add DI value to BX (our sum accumulator)
0x48dd53 MOVQ SI, AX # move SI (previously incremented AX by 1) value to AX (index counter)
0x48dd56 CMPQ DX, AX # compare DX (slice data size) to AX (index counter)
0x48dd59 JL 0x48dd48 # jump back to start if AX (index counter) is less than DX (slice size)
######################## loop end ##########################

因此,这里的主要区别在于,在for 循环的情况下,我们通过切片结构的静态地址访问切片数据指针,并在每次迭代时将其存储在某个通用寄存器中。比较指令被调用为切片数据大小值,我们也是通过切片结构静态地址访问该值。

但在for range循环的情况下,切片数据指针和大小都预先存储在通用寄存器中。所以这里我们每个周期丢失了一条指令。另外,我们不需要每次迭代时从 RAM 或 CPU 缓存中读取切片数据大小。

所以for range循环肯定比for i in slices更快,而且更“安全”。因为如果 slice 在循环迭代期间改变其大小和数据地址(例如来自另一个 goroutine),我们仍然会访问旧的“有效”数据。但当然我们不应该依赖这种行为并消除代码中的任何竞争条件;)

如果当查看for 循环数组

sum := 0
for i := 0; i < len(intsArray); i++ {sum += intsArray[i]
}

以及反汇编:

0x48dd34 XORL AX, AX
0x48dd36 XORL CX, CX
0x48dd38 JMP 0x48dd4f
######################## loop start ##########################
0x48dd3a LEAQ 0x1(AX), DX
0x48dd3e LEAQ main.intsArray(SB), BX # store the address of array in BX
0x48dd45 MOVQ 0(BX)(AX*8), SI
0x48dd49 ADDQ SI, CX
0x48dd4c MOVQ DX, AX
0x48dd4f CMPQ $0x64, AX # here the array size is pre determined at compile time
0x48dd53 JL 0x48dd3a
######################## loop end ##########################

for range循环遍历数组:

sum := 0
for i := range intsArray {sum += intsArray[i]
}

以及反汇编:

0x48dd34 XORL AX, AX
0x48dd36 XORL CX, CX
0x48dd38 JMP 0x48dd4f
######################## loop start ##########################
0x48dd3a LEAQ 0x1(AX), DX
0x48dd3e LEAQ main.intsArray(SB), BX
0x48dd45 MOVQ 0(BX)(AX*8), SI
0x48dd49 ADDQ SI, CX
0x48dd4c MOVQ DX, AX
0x48dd4f CMPQ $0x64, AX
0x48dd53 JL 0x48dd3a
######################## loop end ##########################

我们会发现它们是完全相同的。在这两种情况下,我们每次迭代都会从内存中读取数组的地址并将其存储在 BX 寄存器中。但看起来效率不太高。

但这个怎么样:

sum := 0 
for _, v := range intsArray { sum += v 
}

反汇编之后:

0x48dd49 LEAQ 0x28(SP), DI # 0x28(SP) is the address where our array will be located on the stack
0x48dd4e LEAQ main.intsArray(SB), SI
0x48dd55 NOPW 0(AX)(AX*1)
0x48dd5e NOPW
0x48dd60 MOVQ BP, -0x10(SP)
0x48dd65 LEAQ -0x10(SP), BP
0x48dd6a CALL 0x45e8a4 # runtime.duffcopy call
0x48dd6f MOVQ 0(BP), BP
0x48dd73 XORL AX, AX
0x48dd75 XORL CX, CX
0x48dd77 JMP 0x48dd84
######################## loop start ##########################
0x48dd79 MOVQ 0x28(SP)(AX*8), DX # so now we are accessing our data copy on the stack
0x48dd7e INCQ AX
0x48dd81 ADDQ DX, CX
0x48dd84 CMPQ $0x64, AX
0x48dd88 JL 0x48dd79
######################## loop end ##########################

 由于某种原因,Go 决定将数组复制到堆栈......真的吗?这是作弊。

我尝试将数组大小增加到 1000,但该死的事情仍然认为将所有内容复制到堆栈会更好:)

 0x48dd51 LEAQ 0x28(SP), DI # 0x28(SP) is the address where our array will be located on the stack
0x48dd56 LEAQ main.intsArray(SB), SI # store slice data pointer in SI (from static address)
0x48dd5d MOVL $0x3e8, CX # store slice data size (1000) in CX
0x48dd62 REP; MOVSQ DS:0(SI), ES:0(DI) # Move quadword from SI to DI, repeat CX times
0x48dd65 XORL AX, AX
0x48dd67 XORL CX, CX
0x48dd69 JMP 0x48dd76
######################## loop start ##########################
0x48dd6b MOVQ 0x28(SP)(AX*8), DX
0x48dd70 INCQ AX
0x48dd73 ADDQ DX, CX
0x48dd76 CMPQ $0x3e8, AX
0x48dd7c JL 0x48dd6b
######################## loop end ##########################

主要是使用场景不同

for可以遍历array和slice,遍历key为整型递增的map,遍历string

for range可以完成所有for可以做的事情,却能做到for不能做的,包括

遍历key为string类型的map并同时获取key和value,遍历channel.

我最好的猜测是,由于 Go 多线程特性,编译器决定预先将所有数据(因为我们无论如何都会复制每个值)复制到堆栈中,以在整个for range循环期间保持其完整性并获得一些性能。因此,只有基准测试才能完全反映我们算法性能的真相;)

好的,但是边界检查在哪里呢?panic 在哪里?正如我们所看到的,没有,因为 Go 足够聪明,可以区分根本不存在越界的情况。顺便说一句,它被称为边界检查消除(BCE) 

所以对代码做一个小改动:

sum := 0
for i := 0; i < len(intsSlice)-1; i++ {sum += intsSlice[i+1]
}

现在我们有了:

0x48dd38 XORL AX, AX
0x48dd3a XORL CX, CX
0x48dd3c JMP 0x48dd49
######################## loop start ##########################
0x48dd3e MOVQ 0x8(BX)(AX*8), DX
0x48dd43 ADDQ DX, CX
0x48dd46 MOVQ SI, AX
0x48dd49 MOVQ main.intsSlice+8(SB), DX # store slice data size in DX
0x48dd50 MOVQ main.intsSlice(SB), BX
0x48dd57 LEAQ -0x1(DX), SI
0x48dd5b NOPL 0(AX)(AX*1)
0x48dd60 CMPQ SI, AX
0x48dd63 JGE 0x48dd70 # jump out of the loop if finished
0x48dd65 LEAQ 0x1(AX), SI # SI will get AX (index counter) plus one
0x48dd69 CMPQ SI, DX # out of bounds checking
0x48dd6c JA 0x48dd3e # jump back to loop start if no out of bounds detected
######################## loop end ##########################
0x48dd6e JMP 0x48ddc1 # jump to the panic procedure call
...
0x48ddc1 MOVQ SI, AX
0x48ddc4 MOVQ DX, CX
0x48ddc7 CALL runtime.panicIndex(SB)

最后与 C gcc 编译器进行比较:

int64_t sum = 0;
for (int i = 0; i < sizeof(intsArray) / sizeof(intsArray[0]); i++)
{sum += intsArray[i];
}
gcc -o main.exe -O3 main.c
objdump -S main.exe > main-c-for-i.asm

100401689: lea 0x990(%rip),%rax # 100402020 <intsArray>; store intsArray address in rax
100401690: pxor %xmm0,%xmm0
100401694: lea 0x320(%rax),%rdx # store rax + 800 (array size is 100 * 8 bytes) in rdx (intsArray after end address)
10040169b: nopl 0x0(%rax,%rax,1)
######################## loop start ##########################
1004016a0: paddq (%rax),%xmm0 # adds 2 qwords from rax to xmm0 (128-bit register)
1004016a4: add $0x10,%rax # increments rax (current intsArray address) by 16 bytes
1004016a8: cmp %rax,%rdx # compare rax (current intsArray address) to (intsArray after end address)
1004016ab: jne 1004016a0 <main+0x20> # jump if current intsArray address not equals to intsArray after end address
######################## loop end ##########################
1004016ad: movdqa %xmm0,%xmm1 # copy accumulated 2 qwords to xmm1
1004016b1: psrldq $0x8,%xmm1 # shift xmm1 by 8 bytes right, so the 1-st qword will be at 2-nd qword place
1004016b6: paddq %xmm1,%xmm0 # add shifted 1-st qword from xmm1 to 2-nd qword of xmm0
1004016ba: movq %xmm0,%rax # copy final 2-nd qword to 64 bit rax, so here will be the final result

我们可以看到,循环中只有 4 条指令,并且累加执行速度快了 2 倍,因为使用了paddq指令(将第一个操作数中的2 个打包qword添加到第二个操作数中对应的 2 个打包qword)。 

相关文章:

【go从入门到精通】for和for range的区别

作者简介&#xff1a; 高科&#xff0c;先后在 IBM PlatformComputing从事网格计算&#xff0c;淘米网&#xff0c;网易从事游戏服务器开发&#xff0c;拥有丰富的C&#xff0c;go等语言开发经验&#xff0c;mysql&#xff0c;mongo&#xff0c;redis等数据库&#xff0c;设计模…...

【C语言】【Leetcode】88. 合并两个有序数组

文章目录 一、题目二、思路再思考 一、题目 链接: link 二、思路 这题属于简单题&#xff0c;比较粗暴的做法就是直接比较两个数组&#xff0c;先把第二个数组加到第一个的后面&#xff0c;如何冒泡排序&#xff0c;这种方法简单粗暴但有效&#xff0c;可是不适用于这题&…...

DMA控制器

前言 大家好&#xff0c;我是jiantaoyab&#xff0c;这是我作为学习笔记的25篇&#xff0c;本篇文章给大家介绍DMA。 无论 I/O 速度如何提升&#xff0c;比起 CPU&#xff0c;总还是太慢。如果我们对于 I/O 的操作&#xff0c;都是由 CPU 发出对应的指令&#xff0c;然后等待…...

SQLiteC/C++接口详细介绍sqlite3_stmt类(十)

返回&#xff1a;SQLite—系列文章目录 上一篇&#xff1a;SQLiteC/C接口详细介绍sqlite3_stmt类&#xff08;九&#xff09; 下一篇&#xff1a; SQLiteC/C接口详细介绍sqlite3_stmt类&#xff08;十一&#xff09; 38、sqlite3_column_value sqlite3_column_valu…...

Android 生成Excel文件保存到本地

本文用来记录在安卓中生成Excel文件并保存到本地操作&#xff0c;在网上找了好久&#xff0c;终于找到一个可以用的&#xff0c;虽然代码已经很老的&#xff0c;但亲测可用&#xff01; 项目地址&#xff1a;https://github.com/wanganan/AndroidExcel 可以下载下来修改直接用…...

Hive-技术补充-ANTLR语法编写

一、导读 我们学习一门语言&#xff0c;或外语或编程语言&#xff0c;是不是都是要先学语法&#xff0c;想想这些语言有哪些相同点 1、中文、英语、日语......是不是都有 主谓宾 的规则 2、c、java、python、js......是不是都有 数据类型 、循环 等语法或数据结构 虽然人们在…...

6.使用个人用户登录域控的成员服务器,如何防止个人用户账号的用户策略生效?

&#xff08;1&#xff09;需求&#xff1a; &#xff08;2&#xff09;实战配置步骤 第一步:创建新的策略-并编辑策略 第二步&#xff1a;将策略应用到服务器处在OU 第三步&#xff1a;测试 &#xff08;1&#xff09;需求&#xff1a; 比如域控&#xff0c;或者加入域的…...

模拟算法

例题一 算法思路&#xff1a; 纯模拟。从前往后遍历整个字符串&#xff0c;找到问号之后&#xff0c;就⽤ a ~ z 的每⼀个字符去尝试替换即 可。 例题二 解法&#xff08;模拟 分情况讨论&#xff09;&#xff1a; 算法思路&#xff1a; 模拟 分情况讨论。 计算相邻两个…...

【数据结构刷题专题】—— 二叉树

二叉树 二叉树刷题框架 二叉树的定义&#xff1a; struct TreeNode {int val;TreeNode* left;TreeNode* right;TreeNode(int x) : val(x), left(NULL), right(NULL); };1 二叉树的遍历方式 【1】前序遍历 class Solution { public:void traversal(TreeNode* node, vector&…...

基于AWS云服务构建智能家居系统的最佳实践

在当今智能家居时代,构建一个安全、高性能、可扩展和灵活的智能家居系统已经成为许多公司的目标。亚马逊网络服务(AWS)提供了一系列云服务,可以帮助企业轻松构建和管理智能家居系统。本文将探讨如何利用AWS云服务构建一个智能家居系统,并分享相关的最佳实践。 系统架构概述 该…...

Java零基础-集合:Set接口

哈喽&#xff0c;各位小伙伴们&#xff0c;你们好呀&#xff0c;我是喵手。 今天我要给大家分享一些自己日常学习到的一些知识点&#xff0c;并以文字的形式跟大家一起交流&#xff0c;互相学习&#xff0c;一个人虽可以走的更快&#xff0c;但一群人可以走的更远。 我是一名后…...

数据结构与算法-排序算法

1.顺序查找 def linear_search(iters, val):for i, v in enumerate(iters):if v val:return ireturn 2.二分查找 # 升序的二分查找 def binary_search(iters, val):left 0right len(iters)-1while left < right:mid (left right) // 2if iters[mid] val:return mid…...

SpringBoot 文件上传(三)

之前讲解了如何接收文件以及如何保存到服务端的本地磁盘中&#xff1a; SpringBoot 文件上传&#xff08;一)-CSDN博客 SpringBoot 文件上传&#xff08;二&#xff09;-CSDN博客 这节讲解如何利用阿里云提供的OSS&#xff08;Object Storage Service)对象存储服务保存文件。…...

web渗透测试漏洞流程:红队目标信息收集之资产搜索引擎收集

web渗透测试漏洞流程 渗透测试信息收集---域名信息收集1.域名信息的科普1.1 域名的概念1.2 后缀分类1.3 多重域名的关系1.4 域名收集的作用1.5 DNS解析原理1.6 域名解析记录2. 域名信息的收集的方法2.1 基础方法-搜索引擎语法2.1.1 Google搜索引擎2.1.1.1 Google语法的基本使用…...

UI自动化_id 元素定位

## 导包selenium from selenium import webdriver import time1、创建浏览器驱动对象 driver webdriver.Chrome() 2、打开测试网站 driver.get("你公司的平台地址") 3、使浏览器窗口最大化 driver.maximize_window() 4、在用户名输入框中输入admin driver.find_ele…...

华为OD技术面算法题整理

LeetCode原题 简单 题目编号频次409. 最长回文串 - 力扣(LeetCode)3...

vmware虚拟机下ubuntu扩大磁盘容量

1、扩容&#xff1a; 可以直接在ubuntu setting界面里直接扩容&#xff0c;也可通过vmware命令&#xff0c;如下&#xff1a; vmware提供一个命令行工具&#xff0c;vmware-vdiskmanager.exe&#xff0c;位于vmware的安装目录下&#xff0c;比如 C:/Program Files/VMware/VMwar…...

秋招打卡算法题第一天

一年多没有刷过算法题了&#xff0c;已经不打算找计算机类工作了&#xff0c;但是思来想去&#xff0c;还是继续找吧。 1. 字符串最后一个单词的长度 public static void main(String[] args) {Scanner in new Scanner(System.in);while(in.hasNextInt()){String itemin.nextL…...

BC98 序列中删除指定数字

题目 描述 有一个整数序列&#xff08;可能有重复的整数&#xff09;&#xff0c;现删除指定的某一个整数&#xff0c;输出删除指定数字之后的序列&#xff0c;序列中未被删除数字的前后位置没有发生改变。 数据范围&#xff1a;序列长度和序列中的值都满足 1≤&#xfffd;≤…...

基于Java的学生体质健康管理系统的设计与实现(论文+源码)_kaic

摘 要 随着时代的进步&#xff0c;信息化也在逐渐深入融进我们生活的方方面面。其中也给健康管理带来了新的发展方向。通过对学生体质健康管理的研究与分析发现当下的管理系统还不够全面&#xff0c;系统的性能达不到使用者的要求。因此&#xff0c;本文结合Java的优势和流行性…...

2024年赣州旅游投资集团社会招聘笔试真

2024年赣州旅游投资集团社会招聘笔试真 题 ( 满 分 1 0 0 分 时 间 1 2 0 分 钟 ) 一、单选题(每题只有一个正确答案,答错、不答或多答均不得分) 1.纪要的特点不包括()。 A.概括重点 B.指导传达 C. 客观纪实 D.有言必录 【答案】: D 2.1864年,()预言了电磁波的存在,并指出…...

对WWDC 2025 Keynote 内容的预测

借助我们以往对苹果公司发展路径的深入研究经验&#xff0c;以及大语言模型的分析能力&#xff0c;我们系统梳理了多年来苹果 WWDC 主题演讲的规律。在 WWDC 2025 即将揭幕之际&#xff0c;我们让 ChatGPT 对今年的 Keynote 内容进行了一个初步预测&#xff0c;聊作存档。等到明…...

微服务商城-商品微服务

数据表 CREATE TABLE product (id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 商品id,cateid smallint(6) UNSIGNED NOT NULL DEFAULT 0 COMMENT 类别Id,name varchar(100) NOT NULL DEFAULT COMMENT 商品名称,subtitle varchar(200) NOT NULL DEFAULT COMMENT 商…...

【开发技术】.Net使用FFmpeg视频特定帧上绘制内容

目录 一、目的 二、解决方案 2.1 什么是FFmpeg 2.2 FFmpeg主要功能 2.3 使用Xabe.FFmpeg调用FFmpeg功能 2.4 使用 FFmpeg 的 drawbox 滤镜来绘制 ROI 三、总结 一、目的 当前市场上有很多目标检测智能识别的相关算法&#xff0c;当前调用一个医疗行业的AI识别算法后返回…...

ABAP设计模式之---“简单设计原则(Simple Design)”

“Simple Design”&#xff08;简单设计&#xff09;是软件开发中的一个重要理念&#xff0c;倡导以最简单的方式实现软件功能&#xff0c;以确保代码清晰易懂、易维护&#xff0c;并在项目需求变化时能够快速适应。 其核心目标是避免复杂和过度设计&#xff0c;遵循“让事情保…...

python报错No module named ‘tensorflow.keras‘

是由于不同版本的tensorflow下的keras所在的路径不同&#xff0c;结合所安装的tensorflow的目录结构修改from语句即可。 原语句&#xff1a; from tensorflow.keras.layers import Conv1D, MaxPooling1D, LSTM, Dense 修改后&#xff1a; from tensorflow.python.keras.lay…...

【JavaSE】多线程基础学习笔记

多线程基础 -线程相关概念 程序&#xff08;Program&#xff09; 是为完成特定任务、用某种语言编写的一组指令的集合简单的说:就是我们写的代码 进程 进程是指运行中的程序&#xff0c;比如我们使用QQ&#xff0c;就启动了一个进程&#xff0c;操作系统就会为该进程分配内存…...

比较数据迁移后MySQL数据库和OceanBase数据仓库中的表

设计一个MySQL数据库和OceanBase数据仓库的表数据比较的详细程序流程,两张表是相同的结构,都有整型主键id字段,需要每次从数据库分批取得2000条数据,用于比较,比较操作的同时可以再取2000条数据,等上一次比较完成之后,开始比较,直到比较完所有的数据。比较操作需要比较…...

Python 实现 Web 静态服务器(HTTP 协议)

目录 一、在本地启动 HTTP 服务器1. Windows 下安装 node.js1&#xff09;下载安装包2&#xff09;配置环境变量3&#xff09;安装镜像4&#xff09;node.js 的常用命令 2. 安装 http-server 服务3. 使用 http-server 开启服务1&#xff09;使用 http-server2&#xff09;详解 …...

MySQL 主从同步异常处理

阅读原文&#xff1a;https://www.xiaozaoshu.top/articles/mysql-m-s-update-pk MySQL 做双主&#xff0c;遇到的这个错误&#xff1a; Could not execute Update_rows event on table ... Error_code: 1032是 MySQL 主从复制时的经典错误之一&#xff0c;通常表示&#xff…...