The Go Blog 01:反射的法则(译文)
反思的法则
罗伯-派克
2011 年 9 月 6 日
引言
计算机中的反射是指程序检查自身结构的能力,尤其是通过类型检查自身结构的能力;它是元编程的一种形式。它也是造成混乱的一个重要原因。
在本文中,我们试图通过解释 Go 中的反射是如何工作的来澄清问题。每种语言的反射模型都不尽相同(许多语言根本不支持反射),但本文是关于 Go 的,因此在本文的其余部分,"反射 "一词应理解为 “Go 语言中的反射”。
2022 年 1 月添加的注释:这篇博文写于 2011 年,早于 Go 中的参数多态性(又称泛型)。尽管由于 Go 语言的发展,文章中没有任何重要的内容变得不正确,但为了避免混淆熟悉现代 Go 语言的人,我们还是对一些地方进行了调整。
类型和接口
因为反射建立在类型系统之上,所以我们先来复习一下 Go 中的类型。
Go 是静态类型的。每个变量都有一个静态类型,即在编译时已知并固定的类型:int、float32、*MyType、[]byte 等等。如果我们声明
type MyInt intvar i int
j MyInt
那么 i 的类型是 int,j 的类型是 MyInt。变量 i 和 j 具有不同的静态类型,尽管它们具有相同的底层类型,但如果不进行转换,就不能相互赋值。
接口类型是类型的一个重要类别,它代表固定的方法集。(在讨论反射时,我们可以忽略在多态代码中使用接口定义作为约束)。接口变量可以存储任何具体(非接口)值,只要该值实现了接口的方法。io.Reader和io.Writer是一对著名的例子,它们是io包中的Reader和Writer类型:
// Reader is the interface that wraps the basic Read method.
type Reader interface {Read(p []byte) (n int, err error)
}// Writer is the interface that wraps the basic Write method.
type Writer interface {Write(p []byte) (n int, err error)
}
任何实现了具有此签名的读(或写)方法的类型都被称为实现了 io.Reader(或 io.Writer)。在本讨论中,这意味着io.Reader 类型的变量可以容纳任何具有读取方法的值:
var r io.Reader
r = os.Stdin
r = bufio.NewReader(r)
r = new(bytes.Buffer)
// and so on
需要明确的是,无论 r 的具体值是什么,r 的类型始终是 io.Reader: Go 是静态类型的,r 的静态类型就是 io.Reader。
接口类型的一个极其重要的例子是空接口:
interface{}
或其对应的别名、
any
它表示方法的空集,任何值都可以满足它,因为每个值都有零个或多个方法。
有人说 Go 的接口是动态类型的,但这是一种误导。它们是静态类型的:接口类型的变量总是具有相同的静态类型,即使在运行时存储在接口变量中的值可能会改变类型,该值也总是满足接口的要求。
我们需要准确地理解这一切,因为反射和接口密切相关。
接口的表示
Russ Cox 写过一篇关于 Go 中接口值表示的详细博文。我们没有必要在此重复全部内容,但有必要做一个简化的总结。
接口类型的变量存储一对值:分配给变量的具体值和该值的类型描述符。更准确地说,值是实现接口的底层具体数据项,而类型描述的是该数据项的完整类型。例如,在
var r io.Reader
tty, err := os.OpenFile("/dev/tty", os.O_RDWR, 0)
if err != nil {return nil, err
}
r = tty
从示意代码上看,r 包含 (value, type) 对 (tty、*os.File)。请注意,*os.File 类型实现了 Read 以外的其他方法;尽管接口值只提供了对 Read 方法的访问,但其中的值包含了该值的所有类型信息。这就是我们可以这样做的原因:
w io.Writer
w = r.(io.Writer)
这个赋值中的表达式是一个类型断言;它断言 r 中的项目也实现了 io.Writer,因此我们可以将其赋值给 w。接口的静态类型决定了接口变量可以调用哪些方法,即使里面的具体值可能有更多的方法集。
我们可以继续这样做
var empty interface{}
empty = w
我们的空接口值 empty 将再次包含相同的一对(tty、*os.File)。这很方便:空接口可以容纳任何值,并包含我们所需的关于该值的所有信息。
(这里我们不需要类型断言,因为我们静态地知道 w 满足empty interface{})。在将一个值从 Reader 移到 Writer 的例子中,我们需要明确地使用类型断言,因为 Writer 的方法不是 Reader 方法的子集)。
一个重要的细节是,接口变量内部的变量对总是以 (value, concrete type)的形式存在,而不能以(value, interface type).的形式存在。接口不保存接口值。
现在我们可以进行反射了。
反映的第一定律
1. 反射从接口值到反射对象。
从根本上说,反射只是一种机制,用于检查存储在接口变量中的类型和值对。开始时,我们需要了解包 reflect 中的两种类型: Type and Value。这两种类型可以访问接口变量的内容,而两个简单的函数,即 reflect.TypeOf 和 reflect.ValueOf,可以从接口值中获取 reflect.Type 和 reflect.Value 片段。(此外,从reflect.Value也很容易获取相应的reflect.Type,但我们现在还是把值和类型的概念分开吧)。
让我们从 TypeOf 开始:
package mainimport ("fmt""reflect"
)func main() {var x float64 = 3.4fmt.Println("type:", reflect.TypeOf(x))
}
该程序将打印
type: float64
您可能想知道接口在哪里,因为程序看起来像是将float64变量x传递给reflect.TypeOf,而不是接口值。但它就在那里;正如 godoc 报告的那样,reflect.TypeOf 的签名包含一个空接口:
// TypeOf returns the reflection Type of the value in the interface{}.
func TypeOf(i interface{}) Type
当我们调用 reflect.TypeOf(x) 时,x 首先被存储在一个空接口中,然后将该接口作为参数传递;reflect.TypeOf 会解压该空接口以恢复类型信息。
当然,reflect.ValueOf 函数会恢复值(从这里开始,我们将省略模板,只关注可执行代码):
var x float64 = 3.4
fmt.Println("value:", reflect.ValueOf(x).String())
打印
value: <float64 Value>
(我们明确调用了 String 方法
(我们明确调用 String 方法,是因为默认情况下 fmt 包会挖掘 reflect.Value 以显示其中的具体值。而 String 方法不会)。
reflect.Type 和 reflect.Value 都有很多方法供我们检查和操作。一个重要的例子是,Value 有一个 Type 方法,用于返回 reflect.Value 的 Type。另一个例子是,Type 和 Value 都有一个 Kind 方法,该方法返回一个常量,表示存储的是什么类型的项目: 如 Uint、Float64、Slice 等。此外,Value 上名称为 Int 和 Float 的方法也能让我们抓取存储在其中的值(如 int64 和 float64):
var x float64 = 3.4
v := reflect.ValueOf(x)
fmt.Println("type:", v.Type())
fmt.Println("kind is float64:", v.Kind() == reflect.Float64)
fmt.Println("value:", v.Float())
打印
type: float64
kind is float64: true
value: 3.4
还有 SetInt 和 SetFloat 等方法,但要使用这些方法,我们需要了解可设置性,即下文讨论的反射第三定律的主题。
反射库有几个特性值得一提。首先,为了保持应用程序接口的简洁,Value 的 "getter "和 "setter "方法都是在能容纳值的最大类型上操作的:例如,int64 表示所有带符号的整数。也就是说,Value 的 Int 方法返回的是 int64,而 SetInt 值取值的是 int64;可能有必要转换为相关的实际类型:
var x uint8 = 'x'
v := reflect.ValueOf(x)
fmt.Println("type:", v.Type()) // uint8.
fmt.Println("kind is uint8: ", v.Kind() == reflect.Uint8) // true.
x = uint8(v.Uint())
第二个属性是,反射对象的 Kind 描述的是底层类型,而不是静态类型。如果一个反射对象包含一个用户定义的整数类型的值,如
type MyInt int
var x MyInt = 7
v := reflect.ValueOf(x)
v 的 Kind 仍然是 reflect.Int,尽管 x 的静态类型是 MyInt,而不是 int。换句话说,即使类型可以区分 int 和 MyInt,Kind 也不能。
反射第二定律
2. 反射从反射对象到接口值。
与物理反射一样,Go 中的反射也会产生自己的逆反。
给定一个 reflect.Value,我们可以使用 Interface 方法恢复一个接口值;实际上,该方法将 type and value信息打包回接口表示中,并返回结果:
// Interface returns v's value as an interface{}.
func (v Value) Interface() interface{}
因此,我们可以说
y := v.Interface().(float64) // y 的类型是 float64。
fmt.Println(y)
来打印反射对象 v 所代表的 float64 值。
不过,我们还可以做得更好。fmt.Println、fmt.Printf 等的参数都以空接口值的形式传递,然后由 fmt 包在内部解包,就像我们在前面的示例中所做的那样。因此,要正确打印 reflect.Value 的内容,只需将 Interface 方法的结果传递给格式化打印例程即可:
fmt.Println(v.Interface())
(自本文撰写以来,对 fmt 软件包进行了修改,使其能像这样自动解压缩 reflect.Value,因此我们可以直接说
fmt.Println(v)
就能得到同样的结果,但为了清晰起见,我们在这里保留 .Interface() 调用)。
由于我们的值是 float64,因此我们甚至可以使用浮点格式:
fmt.Printf("value is %7.1e\n", v.Interface())
并在本例中得到
3.4e+00
同样,我们也不需要将 v.Interface() 的结果类型验证为 float64;空接口值内部包含了具体值的类型信息,Printf 将恢复它。
简而言之,Interface 方法就是 ValueOf 函数的逆过程,只不过它的结果总是静态的 interface{} 类型。
重申: 反射从接口值到反射对象,再返回接口值。
反射的第三定律
3. 要修改反射对象,其值必须是可设置的。
第三定律是最微妙、最容易混淆的,但如果我们从第一条原则出发,还是很容易理解的。
下面是一些不起作用但值得研究的代码。
var x float64 = 3.4
v := reflect.ValueOf(x)
v.SetFloat(7.1) // Error: will panic.
如果运行这段代码,会出现以下提示信息
panic: reflect.Value.SetFloat using unaddressable value
问题不在于值 7.1 不可寻址,而在于 v 不可设置。可设置性是reflection Value的一个属性,并非所有reflection Value都具有该属性。
在我们的例子中,Value 的 CanSet 方法会报告 Value 的可设置性、
var x float64 = 3.4
v := reflect.ValueOf(x)
fmt.Println("settability of v:", v.CanSet())
打印
settability of v: false
在不可设置的值上调用设置方法是一个错误。但什么是可设置性呢?
可设置性有点像可寻址性,但更严格。它是反射对象可以修改用于创建反射对象的实际存储空间的属性。可设置性取决于反射对象是否持有原始项目。当我们说
var x float64 = 3.4
v := reflect.ValueOf(x)
时,我们向 reflect.ValueOf 传递了 x 的副本,因此作为 reflect.ValueOf 参数创建的接口值是 x 的副本,而不是 x 本身。因此,如果语句
v.SetFloat(7.1)
会更新存储在反射值中的 x 的副本,而 x 本身不会受到影响。这样做既混乱又无用,因此是非法的,而可设置性正是用来避免这一问题的属性。
如果这看起来很奇怪,其实不然。实际上,这是一个我们熟悉的情况,只是披上了不寻常的外衣。想想把 x 传递给函数
f(x)
我们不会指望 f 能够修改 x,因为我们传递的是 x 值的副本,而不是 x 本身。如果我们想让 f 直接修改 x,就必须向函数传递 x 的地址(即指向 x 的指针):
f(&x)
这既简单又熟悉,反射也是如此。如果我们想通过反射修改 x,就必须给反射库一个指向我们要修改的值的指针。
让我们开始吧。首先,我们像往常一样初始化 x,然后创建一个指向它的反射值,称为 p。
var x float64 = 3.4
p := reflect.ValueOf(&x) // Note: take the address of x.
fmt.Println("type of p:", p.Type())
fmt.Println("settability of p:", p.CanSet())
目前的输出结果是
type of p: *float64
settability of p: false
反射对象 p 不可设置,但我们要设置的不是 p,而是(实际上)*p。为了获取 p 指向的内容,我们调用了 Value 的 Elem 方法,该方法通过指针进行间接操作,并将结果保存在名为 v 的reflection Value 中:
v := p.Elem()
fmt.Println("settability of v:", v.CanSet())
正如输出结果所示,现在 v 是一个可设置的反射对象、
settability of v: true
由于 v 代表 x,我们终于可以使用 v.SetFloat 来修改 x 的值了:
v.SetFloat(7.1)
fmt.Println(v.Interface())
fmt.Println(x)
正如预期的那样,输出结果是
7.1
7.1
反射可能很难理解,但它确实在做语言所做的事情,尽管通过反射类型和值可以掩盖正在发生的事情。请记住,反射值需要某些东西的地址,以便修改它们所代表的内容。
Structs
在我们前面的示例中,v 本身并不是一个指针,它只是从指针派生出来的。出现这种情况的常见方法是使用反射来修改结构体的字段。只要我们有结构体的地址,就可以修改它的字段。
下面是一个分析 struct value t 的简单示例。我们用结构体的地址创建反射对象,因为我们稍后要修改它。然后,我们将 typeOfT 设置为其类型,并使用直接的方法调用遍历字段(详见包 reflect)。请注意,我们从结构类型中提取了字段的名称,但字段本身是普通的 reflect.Value 对象。
type T struct {A intB string
}
t := T{23, "skidoo"}
s := reflect.ValueOf(&t).Elem()
typeOfT := s.Type()
for i := 0; i < s.NumField(); i++ {f := s.Field(i)fmt.Printf("%d: %s %s = %v\n", i,typeOfT.Field(i).Name, f.Type(), f.Interface())
}
该程序的输出结果是
0: A int = 23
1: B string = skidoo
这里还顺便介绍了一个关于可设置性的要点:T 的字段名是大写的(导出),因为只有结构体的导出字段才是可设置的。
因为 s 包含一个可设置的反射对象,所以我们可以修改结构体的字段。
s.Field(0).SetInt(77)
s.Field(1).SetString("Sunset Strip")
fmt.Println("t is now", t)
结果如下
t is now {77 Sunset Strip}
如果我们修改程序,使 s 是根据 t 而不是 &t 创建的,那么对 SetInt 和 SetString 的调用就会失败,因为 t 的字段将不可设置。
Conclusion
这里又是反射法则:
Reflection goes from interface value to reflection object.
Reflection goes from reflection object to interface value.
To modify a reflection object, the value must be settable.
一旦你理解了这些定律,Go中的反射就会变得更容易使用,尽管它仍然很微妙。这是一个强大的工具,除非必要,否则应该小心使用。
还有很多我们没有涉及到的问题——在channel上发送和接收、分配内存、使用slices和map、调用方法和函数——但是这篇文章已经足够长了。我们将在后面的文章中讨论其中的一些主题。
相关文章:
The Go Blog 01:反射的法则(译文)
反思的法则 罗伯-派克 2011 年 9 月 6 日 引言 计算机中的反射是指程序检查自身结构的能力,尤其是通过类型检查自身结构的能力;它是元编程的一种形式。它也是造成混乱的一个重要原因。 在本文中,我们试图通过解释 Go 中的反射是如何工作的…...
Visual Studio Code前端开发插件推荐
引言 Visual Studio Code(简称VS Code)是一款轻量级且强大的开源代码编辑器,广受前端开发者的喜爱。其丰富的插件生态系统为前端开发提供了许多便利和增强功能的插件。本篇博客将向大家推荐一些在前端开发中常用且优秀的插件,并提…...
jps(JVM Process Status Tool):虚拟机进程状况工具
jps(JVM Process Status Tool):虚拟机进程状况工具 列出正在运行的虚拟机进程,并显示虚拟机执行主类名称(Main Class,main()函数所在的类)以及这些进程的本地虚拟机唯一ID(LVMID&am…...
初阶c语言:实战项目三子棋
前言 大家已经和博主学习有一段时间了,今天讲一个有趣的实战项目——三子棋 目录 前言 制作菜单 构建游戏选择框架 实现游戏功能 模块化编程 初始化棋盘 打印棋盘 玩家下棋 电脑下棋 时间戳:推荐一篇 C语言生成随机数的方法_c语言随机数_杯浅…...
计网第三章(数据链路层)(三)
一、点对点协议PPP 在第一篇里有提到数据链路层的信道分为两种:点对点信道和广播信道。 PPP协议就属于点对点信道上的协议。 如果对前面数据链路层的三个基本问题了解的比较透彻,那么这一块很多东西都很好理解。 从考试的角度来讲,PPP协议…...
蓝桥杯每日N题 (砝码称重)
大家好 我是寸铁 希望这篇题解对你有用,麻烦动动手指点个赞或关注,感谢您的关注 不清楚蓝桥杯考什么的点点下方👇 考点秘籍 想背纯享模版的伙伴们点点下方👇 蓝桥杯省一你一定不能错过的模板大全(第一期) 蓝桥杯省一你一定不…...
Opencv 视频的读取与写入
目录 前言 通过路径获取视频内容 获取视频内容 检查是否正确打开 循环播放 完整代码 从摄像头读取视频数据 获取视频设备 其他与直接读取视频一致 完整实例 录制视频 用于创建视频编解码器的四字符码(FourCC) cv2.VideoWriter() 将视频帧…...
LeetCode 833. Find And Replace in String【字符串,哈希表,模拟】1460
本文属于「征服LeetCode」系列文章之一,这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁,本系列将至少持续到刷完所有无锁题之日为止;由于LeetCode还在不断地创建新题,本系列的终止日期可能是永远。在这一系列刷题文章…...
Cesium轨迹漫游及视角切换
飞行漫游,就是让Camera飞行。Camera有一些方法可以实现位置、视角的调整,比如flyTo,setView方法。但这些方法并不能沿着我们想要的路径调整,在通过插值的方法不停的调用setView,但这样会造成视图卡顿,而且计…...
构建去中心化微服务集群,满足高可用性和高并发需求的实践指南!
随着互联网技术的不断发展,微服务架构已经成为了开发和部署应用程序的一种主流方式。然而,当应用程序需要满足高可用性和高并发需求时,单一中心化的微服务架构可能无法满足性能和可靠性的要求。因此,构建一个去中心化的微服务集群…...
开集输出和开漏输出
首先指明一下以下8中GPIO输入输出模式: GPIO_Mode_AIN 模拟输入; GPIO_Mode_IN_FLOATING 浮空输入; GPIO_Mode_IPD 下拉输入; GPIO_Mode…...
解决内网GitLab 社区版 15.11.13项目拉取失败
问题描述 GitLab 社区版 发布不久,搭建在内网拉取项目报错,可能提示 unable to access https://github.comxxxxxxxxxxx: Failed to connect to xxxxxxxxxxxxxGit clone error - Invalid argument error:14077438:SSL routines:SSL23_GET_S 15.11.13ht…...
【MySQL--->表的约束】
文章目录 [TOC](文章目录) 一、表的约束概念二、空属性约束三、default约束四、zerofill约束五、主键约束六、auto_increment(自增长)约束七、唯一键约束八、外键约束 一、表的约束概念 表通过约束可以保证插入数据的合法性,本质是通过技术手段,保证插入数据收约束,保证数据的…...
github中Keyless Google Maps API在网页中显示地图和标记 无需api key
使用Google Maps API在网页中显示地图和标记的示例博客。以下是一个简单的示例: C:\pythoncode\blog\google-map-markers-gh-pages\google-map-markers-gh-pages\index.html 介绍: 在本篇博客中,我们将学习如何使用Google Maps API在网页中…...
ComPDFKit PDF SDK for Windows Crack
ComPDFKit PDF SDK for Windows Crack 添加了在创建文本框时调整默认属性的支持。 增加了对调整PDF大小时调整宽度的支持。 添加了对编辑文本时更多快捷方式的支持。 优化了文本输入,并将字体样式与原始文本相匹配。 在内容编辑器模式下复制和粘贴时优化了UI交互。 …...
React+Typescript 状态管理
好 本文 我们来说说状态管理 也就是我们的 state 我们直接顺便写一个组件 参考代码如下 import * as React from "react";interface IProps {title: string,age: number }interface IState {count:number }export default class hello extends React.Component<I…...
stable diffusion 运行时报错: returned non-zero exit status 1.
运行sh run.sh安装stable diffusion时报错:ImportError: cannot import name builder from google.protobuf.internal (stable-diffusion-webui/venv/lib/python3.8/site-packages/google/protobuf/internal/__init__.py) 原因:python版本过低࿰…...
el-popover弹窗修改三角样式或者位置
el-popover中设置类名 popper-class"filepopver",我这位置是placement"top-start" <el-popover placement"top-start" popper-class"filepopver" class"filename" width"300" trigger"hover&q…...
Linux驱动开发之点亮三盏小灯
头文件 #ifndef __HEAD_H__ #define __HEAD_H__//LED1和LED3的硬件地址 #define PHY_LED1_MODER 0x50006000 #define PHY_LED1_ODR 0x50006014 #define PHY_LED1_RCC 0x50000A28 //LED2的硬件地址 #define PHY_LED2_MODER 0x50007000 #define PHY_LED2_ODR 0x50007014 #define…...
【SA8295P 源码分析】71 - QAM8295P 原理图参考设计 之 MIPI DSI 接口硬件原理分析
【SA8295P 源码分析】71 - QAM8295P 原理图参考设计 之 MIPI DSI 接口硬件原理分析 一、MIPI-DSI 接口介绍二、高通参考硬件原理图分析:ANX7625 桥接芯片方案2.1 高通参考设计:两路 4-Lane DSI 接口2.2 高通参考设计:DSI0 硬件原理图,将 4 Lane DSI数据通过 ANX7625 桥接芯…...
macOS(m1/m2)破解Sublime Text和Navicat16
破解Sublime Text 说明:全程使用的是终端操作 1. 下载Sublime Text,建议使用brew下载 2. 进入到下载的app的文件夹 cd "/Applications/Sublime Text.app/Contents/MacOS/"3. 执行以下操作以确认版本是否匹配 md5 -q sublime_text | grep -i…...
【排排站:探索数据结构中的队列奇象】
本章重点 队列的概念及结构 队列的实现方式 链表方式实现栈接口 队列面试题 一、队列的概念及结构 队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出 FIFO(First In First Out) 入队列&#x…...
Mac OS 中JDK 环境(jdk 1.8.0_831)安装配置、环境变量配置及卸载操作
前言: 摊牌了,本来就有点喜新厌旧的我,特意把系统和开发环境都拉到比较高,想试验一下兼容性和某些新特性,探索了一下新大陆,也见识了各种光怪陆离的妖魔鬼怪。 因为要着手云平台项目的重构改版和新系统的架…...
[JAVAee]Tomcat - Servlet
目录 Tomcat Servlet的工作 创建Servlet ①项目 ②依赖 ③目录 ④代码 ⑤打包 ⑥部署 ⑦验证 Servlet的运行原理 Servlet API HttpServlet 方法 处理Get/POST请求 HttpServletRequest 方法 获取请求中的信息 获取GET请求中的参数 获取POST请求中的参数…...
MAC钓鱼并Root权限上线CS并权限维持,以及所有的坑如何解决
本文转载于:https://www.freebuf.com/articles/web/350592.html 作者:文鸯涂鸦智能安全实验室 制作MAC 一、下载工具 首先从github上下载CrossC2。链接:https://github.com/gloxec/CrossC2/releases/tag/v3.1.0。 根据你CS客户端的操作系统选…...
浅谈OCR中的David Shepard
在OCR(Optical Character Recognition,光学字符识别)中,David Shepard是一种早期的OCR技术,也被称为Shepards Method。 David Shepard是该OCR方法的原始作者。这种方法基于边界追踪算法,用于识别印刷体文本…...
draw.io导出矢量图到word报错text is not svg - cannot display
先参考https://blog.csdn.net/a625750076/article/details/126384831 如果不行,可能是转存的问题 解决方法:直接在draw.io上操作 第一步 第二步 然后再word中粘贴,依旧是矢量图哦!...
JVM加强
目录 JVM运行时的数据区(内存结构): 线程独享: 线程共享: 什么时候会内存溢出 JVM有哪些垃圾回收算法 GC如何判断对象可以被回收 典型的垃圾回收器 CMS: G1: 类加载器和双亲委派机制&a…...
解决Oracle中XML插入数据时的空格问题
🌷🍁 博主猫头虎 带您 Go to New World.✨🍁 🦄 博客首页——猫头虎的博客🎐 🐳《面试题大全专栏》 文章图文并茂🦕生动形象🦖简单易学!欢迎大家来踩踩~🌺 &a…...
微服务中间件--分布式事务
分布式事务 a.理论基础1) CAP定理2) BASE理论 b.Seata1) XA模式1.a) 实现XA模式 2) AT模式3) TCC模式3.a) 代码实现 4) Saga模式5) 四种模式对比6) TC的异地多机房容灾架构 a.理论基础 1) CAP定理 分布式系统有三个指标: Consistency(一致性ÿ…...
专业网站建设市场/友链
本题就是求半交平面的交包含哪些直线,而且有点特殊(一般的半交平面用双端队列,因为可能转到最开始的直线,但本题不会,所以只需要一端操作就行了)。 1 /**************************************************…...
怎么用记事本做网站/搜索引擎seo关键词优化效果
尼姆达蠕虫导致网络访问变慢 故障现象Internet访问速度明显变慢,远程无法telnet至路由器。借助Console端口访问路由器,使用show cpu命令查看CPU占用率,高达95%以上,同时,IP Input占用了非常高的CPU占用率。由此&#…...
网站建设流程效果/惠州seo按天付费
为什么80%的码农都做不了架构师?>>> 这个错误的产生原因是代码中使用了since 1.4, 标记代码只能在jdk 1.4 之后的版本中才能使用,但是项目的编译环境在 1.4 之前,所以idea 给出了这个错误提示。 修复方式:…...
做网站是那个语言写的/金华网站推广
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼ShowWindow(hWnd, iCmdShow);UpdateWindow(hWnd);while (GetMessage(&msg, NULL, 0, 0)){TranslateMessage(&msg);DispatchMessage(&msg);}return msg.wParam;}LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPA…...
网站建设运营费用包括哪些/网络营销好找工作吗
阅读本文大概需要 3.5 分钟。 本篇是设计模式系列的开篇,虽然之前也写过相应的文章,但是因为种种原因后来断掉了,而且发现之前写的内容也很渣,不够系统。 所以现在打算重写,加上距离现在也有一段时间了,也算…...
淄博营销网站建设公司/做网络推广的公司
/************关于本文档******************************************** *filename: Linux下各类TCP网络服务器的实现源代码 *purpose: 记录Linux下各类tcp服务程序源代码 *wrote by: zhoulifa(zhoulifa163.com) 周立发(http://zhoulifa.bokee.com) Linux爱好者 Linux知识传…...