Go panic的学习
一、前言
我们的应用程序常常会出现异常,包括由运行时检测到的异常或者应用开发者自己抛出的异常。
- 异常在一些其他语言中,如c++、java,被叫做Exception,主要由抛出异常和捕获异常两部分组成。
- 异常在go语言中,叫做panic,且由panic和recover方法组成,panic用来抛出,recover用来从panic中恢复。
1.1 panic实例分析
以下是一段简单的panic和recover使用示例:
package mainimport "fmt"func main() {f()fmt.Println("Returned normally from f.")
}func f() {/*defer func() {if r := recover(); r != nil {fmt.Println("Recovered in f", r)}}()*/fmt.Println("Calling g.")g(0)fmt.Println("Returned normally from g.")
}func g(i int) {fmt.Println("Printing in g", i)panic(i)fmt.Println("After panic in g", i)
}
我们先把defer recover部分注释,运行结果如下:
Calling g.
Printing in g 0
panic: 0goroutine 1 [running]:
main.g(0x4b14a0)/tmp/sandbox2444947193/prog.go:18 +0x94
main.f()/tmp/sandbox2444947193/prog.go:12 +0x5d
main.main()/tmp/sandbox2444947193/prog.go:6 +0x19Program exited.
可以看到程序运行到g方法的第二行时,产生的panic导致进程异常退出
,后续的代码都没有执行。
再把recover注释部分打开,运行结果为:
Calling g.
Printing in g 0
Recovered in f 0
Returned normally from f.Program exited.
f方法中的recover捕获了panic,打印了panic传递的参数,并且main方法是正常返回的。g方法panic之后的代码没有执行。
1.2 官方翻译
panic是go的内置函数,它可以终止程序的正常执行流程并发出panic。
比如:当函数F调用panic,F的执行将被终止,并返回到调用者。对调用者而言,F就像调用者直接调用了panic。该过程一直跟随堆栈向上,直到当前goroutine中的所有函数都返回,此时 程序崩溃
panic可以通过直接调用panic产生。同时也可能由运行时的错误所产生,例如数组越界访问。
recover是go语言的内置函数,它的唯一作用是可以从panic中重新控制goroutine的执行。recover必须通过defer来运行
。
在正常的执行流程中,调用recover将会返回nil且没有什么其他的影响。但是如果当前的goroutine产生了panic,recover将会捕获到panic抛出的信息,同时恢复其正常的执行流程。
小结
- panic可以令程序崩溃(异常退出)
- recover可以让程序从panic中恢复,并正常运行
- 即使单个goroutine中发生了panic,也会使整个进程崩溃
- recover必须通过defer来运行
二、实现原理
2.1 panic从哪来
我们可以手动调用内置函数panic,但是那些空指针、数组越界等运行时panic是如何被检测到的,下面针对这一问题做一些代码调试
2.1.1 常见的几种panic
- 空指针 invalid memory address or nil pointer dereference
- 数组越界 index out of range;slice bounds out of range
- 除数为零 integer divide by zero
- 自定义panic
2.1.2 追踪panic来源
测试代码
package main
func main() {a := 0testDivide(a) //除零//testOutRange() //越界//testNil() //空指针//panic("666") //自定义panic
}
func testDivide(a int) {b := 10 / a_ = b
}
func testOutRange() {var a []inta[0] = 2
}
func testNil() {var a *int*a = 1
}
调试代码
与linux平台下的gdb调试工具类似,dlv用来调试go语言编写的程序。
dlv是一个命令行工具,它包含了多个调试命令,例如运行程序、下断点、打印变量、step in、step out等。我们常用的go语言编辑器,如vscode、golang等的可视化调试也是调用dlv。
找出panic是怎么产生的:
这里我们先给出结论,具体调试过程产生的代码,请往下看
调试自定义panic方法:
- 在8行处下断点
- 打印main方法的汇编代码
- 可以看到panic方法编译后实质是runtime包中的gopanic方法
使用dlv调试testDivide中的代码,有以下几个关键步骤:
- 在12行处下断点
- 打印testDivide方法的汇编代码
- testDivide方法中测试参数a的值是否为零
- 如果为零,则调用runtime包的panicdivide方法
- 调用runtime包的panicdivide方法
- panicdivide方法调用了panic
- 打印panicdivide的汇编代码,panic方法编译后实质是runtime包中的gopanic方法
所以其实panic方法实际调用了runtime.gopanic
- 编译后的testDivide方法中除了正常的除法逻辑,编译器塞入了判断除数是否为零的代码分支,当除数为零则进入panic流程,与自定义panic相同,同样调用了runtime.gopanic
- 其他数组越界及空指针,也都是调用了runtime.gopanic进入panic流程,不同的是:数组越界与除数为零相似,是通过编译器塞入判断分支进行越界检测;而空指针是通过访问非法地址产生中断进入panic流程。
小结
- panic可以由开发者调用内置函数抛出
- 编译器将检测异常的代码加入到程序中,会出现异常时抛出
- 某些非法指令产生中断,并由中断处理函数抛出
2.2 panic到哪去
2.2.1 panic后的处理流程
由于panic和defer有着难解难分的关系,我们先了解一下defer。
defer定义的官翻:
defer语句将函数调用保存到一个列表上。保存的调用列表在当前函数返回前执行。Defer通常用于简化执行各种清理操作的函数。
通俗地说,就是defer保证函数调用不管在什么情况下(即使当前函数发生panic),在当前函数返回前必然执行。另外defer的函数调用符合先进后出的规则,即先defer的函数后执行。
我们看一个示例程序,它是第一节示例程序的升级版本,方法g中会调用自身:
package mainimport "fmt"func main() {defer func() {fmt.Println("defer in main")}()f()fmt.Println("Returned normally from f.")
}func f() {/*defer func() {if r := recover(); r != nil {fmt.Println("Recovered in f", r)}}()*/defer func() {fmt.Println("defer in f")}()fmt.Println("Calling g.")g(0)fmt.Println("Returned normally from g.")
}func g(i int) {if i > 3 {fmt.Println("Panicking!")panic(fmt.Sprintf("%v", i))}defer fmt.Println("Defer in g", i)fmt.Println("Printing in g", i)g(i + 1)
}
程序运行结果如下:
作者:刘玮
链接:https://www.zhihu.com/question/295517993/answer/2421882834
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。Calling g.
Printing in g 0
Printing in g 1
Printing in g 2
Printing in g 3
Panicking!
Defer in g 3
Defer in g 2
Defer in g 1
Defer in g 0
defer in f
defer in main
panic: 4goroutine 1 [running]:
main.g(0x4)/tmp/sandbox2114608904/prog.go:30 +0x1ec
main.g(0x3)/tmp/sandbox2114608904/prog.go:34 +0x136
main.g(0x2)/tmp/sandbox2114608904/prog.go:34 +0x136
main.g(0x1)/tmp/sandbox2114608904/prog.go:34 +0x136
main.g(0x0)/tmp/sandbox2114608904/prog.go:34 +0x136
main.f()/tmp/sandbox2114608904/prog.go:23 +0x7f
main.main()/tmp/sandbox2114608904/prog.go:9 +0x3fProgram exited
从运行结果可以观察到defer的作用,即使方法g中当i为4时发生了panic,每个defer的函数调用依然正常被执行了,而且是先进后出的顺序被执行。就像是每次defer时,将被defer的函数调用push到一个栈数据结构中,当返回时,再从栈中挨个将defer的函数pop出来并执行。
recover函数调用必须使用defer关键字,就是因为defer的函数调用必然会被执行。可以将以上实例中defer recover部分打开观察输出,与第一节中defer recover输出类似,程序可以正常执行并正常退出。
2.2.2 源码分析
我们再对源码做一下简单分析,以加深对panic及recover处理流程的理解。
首先简单了解下有关defer的一对方法:deferproc和deferreturn。
- deferproc即defer关键字的实现,它将defer的函数调用push到当前goroutine中的defer链表头部
- deferreturn,当一个函数中包含defer操作,编译器将在函数返回前插入一条deferreturn调用,deferreturn会将当前函数中defer的函数调用依次执行完毕
panic方法对应的实现为runtime.gopanic,recover方法对应的实现为runtime.gorecover。
源码如下(为了简化理解,省略了很多分支判断,只保留主流程的代码):
func gopanic(e interface{}) {//获取当前goroutine的对象gpgp := getg()...//将当前panic添加到gp的panic链表头部var p _panicp.arg = ep.link = gp._panicgp._panic = (*_panic)(noescape(unsafe.Pointer(&p)))...//循环执行defer链表中的函数for {//获取gp的defer链表d := gp._deferif d == nil {//如果没有defer,退出循环break}...done := true...//执行defer的函数调用var regs abi.RegArgsreflectcall(nil, unsafe.Pointer(d.fn), deferArgs(d), uint32(d.siz), uint32(d.siz), uint32(d.siz), ®s)...p.argp = nild._panic = nil...if done {//清理defer对象,并设置下一个defer对象到gp的defer链表头部d.fn = nilgp._defer = d.linkfreedefer(d)}if p.recovered {//如果defer运行了recover函数,调用内置的recovery函数恢复调用//recovery函数会将当前的调用栈改变到deferreturn,从而使得程序可以继续正常运行...gp.sigcode0 = uintptr(sp)gp.sigcode1 = pcmcall(recovery)throw("recovery failed") // mcall should not return}}//如果没有recover,defer执行完毕,打印panic信息,并退出进程preprintpanics(gp._panic)fatalpanic(gp._panic) // should not return*(*int)(nil) = 0 // not reached
}//recover方法的实现
func gorecover(argp uintptr) interface{} {gp := getg()p := gp._panic...//recover方法仅有的一个作用,将recovered置为truep.recovered = truereturn p.arg
}
小结
- panic处理过程中会检测是否有defer的函数调用
- 如果有,按照先进后出的顺序依次执行
- 如果defer中有recover调用,则将调用栈修改到deferreturn,使得程序正常执行
- 否则当defer的函数调用执行完后,打印panic信息,进程退出
2.3 panic 打印信息
最后我们通过一个简单的例子,看一下recover后如何打印panic信息,及如何阅读panic信息
示例是一个除零的panic:
- recover后,调用printPanicInfo方法
- printPanicInfo使用runtime.Stack方法收集调用堆栈信息
- r为recover返回的参数,即panic传入的参数,一般为panic的具体原因,本示例为:runtime error: integer divide by zero
- 将panic原因和堆栈信息拼接并打印
package main
import ("fmt""runtime"
)
func main() {f()
}
func f() {defer func() {if r := recover(); r != nil {printPanicInfo(r)}}()g()
}
func g() {a := 10var b inta = a / b
}
func printPanicInfo(r interface{}) {buf := make([]byte, 64<<10)buf = buf[:runtime.Stack(buf, false)]s := fmt.Sprintf("%s\n%s", r, buf)fmt.Println(s)
}
输出为:
作者:刘玮
链接:https://www.zhihu.com/question/295517993/answer/2421882834
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。//panic的原因
runtime error: integer divide by zero
//goroutine的id
goroutine 1 [running]:
//下面是runtime.Stack方法调用时的调用堆栈链,方法名称和方法被调用的文件行数成对出现
main.printPanicInfo(0x4b78c0, 0x572a10) //方法名称E:/xxx/liuwei/test/main.go:29 +0x74 //方法所在的文件和行数
main.f.func1()E:/xxx/liuwei/test/main.go:15 +0x59
panic(0x4b78c0, 0x572a10)C:/go1.13/go/src/runtime/panic.go:679 +0x1c0 //panic被调用
main.g(...)E:/xxx/liuwei/test/main.go:24 //发生panic的代码行数
main.f()E:/xxx/liuwei/test/main.go:18 +0x50
main.main()E:/xxx/liuwei/test/main.go:9 +0x27
打印的信息中主要由panic原因
和调用堆栈
组成,我们阅读堆栈信息时,可以首先找到runtime.panic
,它的下一条堆栈
记录就是发生panic的代码具体行数。然后再结合panic的原因信息,一般会很快了解到panic发生的原因。
另外除了panic之外还有一种fatalpanic,这种严重的异常无法使用recover恢复,一般是运行时检测到不可恢复的操作时抛出。例如发生map并发写时会throw(“concurrent map writes”),导致进程崩溃。
特别提示
- 因为Golang的gorotuine机制,panic在不同的gorotuine里面,是单独的,并不是整体处理。可能一个地方挂了,就会整体挂掉,这个要非常小心。
三、总结
- panic() 会退出进程,是因为调用了 exit 的系统调用;
- recover() 并不是说只能在 defer 里面调用,而是只能在 defer 函数中才能生效,只有在 defer 函数里面,才有可能遇到 _panic 结构;
- recover() 所在的 defer 函数必须和 panic 都是挂在同一个 goroutine 上,不能跨协程,因为 gopanic 只会执行当前 goroutine 的延迟函数;
- panic 的恢复,就是重置 pc 寄存器,直接跳转程序执行的指令,跳转到原本 defer 函数执行完该跳转的位置(deferreturn 执行),从 gopanic 函数中跳出,不再回来,自然就不会再 fatalpanic;
- panic 为啥能嵌套?这个问题就像是在问为什么函数调用可以嵌套一样,因为这个本质是一样的。
参考资料
6. 深度细节 | Go 的 panic 秘密都在这
7. go panic 的实现原理
相关文章:
Go panic的学习
一、前言 我们的应用程序常常会出现异常,包括由运行时检测到的异常或者应用开发者自己抛出的异常。 异常在一些其他语言中,如c、java,被叫做Exception,主要由抛出异常和捕获异常两部分组成。异常在go语言中,叫做pani…...
讲解Linux中samba理论讲解及Linux共享访问
♥️作者:小刘在C站 ♥️个人主页:小刘主页 ♥️每天分享云计算网络运维课堂笔记,努力不一定有收获,但一定会有收获加油!一起努力,共赴美好人生! ♥️夕阳下,是最美的绽放࿰…...
【C++笔试强训】第三十二天
🎇C笔试强训 博客主页:一起去看日落吗分享博主的C刷题日常,大家一起学习博主的能力有限,出现错误希望大家不吝赐教分享给大家一句我很喜欢的话:夜色难免微凉,前方必有曙光 🌞。 💦&a…...
OpenAI GPT-4震撼发布:多模态大模型
OpenAI GPT-4震撼发布:多模态大模型发布要点GPT4的新功能GPT-4:我能玩梗图GPT4:理解图片GPT4:识别与解析图片内容怎样面对GPT4申请 GPT-4 API前言: 🏠个人主页:以山河作礼。 📝📝:本文章是帮助大家更加了…...
手把手教你 在linux上安装kafka
目录 1. 准备服务器 2. 选一台服务器配置kafka安装包 2.1 下载安装包 2.2 解压安装包 2.3 修改配置文件 3. 分发安装包到其他机器 4. 修改每台机器的broker.id 5. 配置环境变量 6. 启停kafka服务 6.1 启动kafak服务 6.2 停止kafka服务 1. 准备服务器 1.买几台云服务…...
Spring Cloud(微服务)学习篇(五)
Spring Cloud(微服务)学习篇(五) 1 nacos配置文件的读取 1.1 访问localhost:8848/index.html并输入账户密码后进入nacos界面并点击配置列表 1.2 点击右侧的号 1.3 点击加号后,进入新建配置界面,并做好如下配置 1.4 往下翻动,点击发布按钮 1.5 发布成功后的界面 1.6 在pom.xml…...
道阻且长,未来可期,从GPT-4窥得通用人工智能时代的冰山一角!
大家这两天是不是又被满屏的ChatGPT相关的文章信息给轰炸得不轻,说实话,我真的对ChatGPT的热度如此经久不衰这个问题非常感兴趣。从去年刚面世时,小范围内造成的行业震荡,到今年二月份铺天盖地得铺舆论造势,引发全民热…...
百度将?百度已!
仿佛一夜之间,创业公司OpenAI旗下的ChatGPT就火遍全球。这是一场十分罕见的科技盛宴。下到普通用户,上到各科技大厂都在讨论ChatGPT的前景,国外的微软、谷歌,国内的百度、腾讯、阿里等等都在布局相关业务。比尔盖茨更是称ChatGPT与…...
内核实验(三):编写简单Linux内核模块,使用Qemu加载ko做测试
文章目录一、篇头二、QEMU:挂载虚拟分区2.1 创建 sd.ext4.img 虚拟分区2.2 启动 Qemu2.3 手动挂载 sd.ext4.img三、实现一个简单的KO3.1 目录文件3.2 Makefile3.3 编译3.3.1 编译打印3.3.2 生成文件3.4 检查:objdump3.4.1 objdump -dS test\_1.ko3.4.2 o…...
女子举重问题
一、问题的描述 问题及要求 1、搜集各个级别世界女子举重比赛的实际数据。分别建立女子举重比赛总成绩的线性模型、幂函数模型、幂函数改进模型,并最终建立总冠军评选模型。 应用以上模型对最近举行的一届奥运会女子举重比赛总成绩进行排名,并对模型及…...
试题 历届真题 循环小数【第十一届】【决赛】【Python】
试题 历届真题 循环小数【第十一届】【决赛】【Python】 题目来源:第十一届蓝桥杯决赛 http://lx.lanqiao.cn/problem.page?gpidT2891 资源限制 内存限制:256.0MB C/C时间限制:1.0s Java时间限制:3.0s Python时间限制ÿ…...
关于类型转换
隐式转换先看个例子int a {500}; unsigned b {1000}; std::cout<<a-b;这里的输出结果并不为-500。因为最后输出结果的类型自动转换成了unsigned,unsigned是正整数型类型转换顺序表(由高到低)long doubledoublefloatunsigned long long long longunsigned long…...
蓝桥杯冲击-02约数篇(必考)
文章目录 前言 一、约数是什么 二、三大模板 1、试除法求约数个数 2、求约数个数 3、求约数之和 三、真题演练 前言 约数和质数一样在蓝桥杯考试中是在数论中考察频率较高的一种,在省赛考察的时候往往就是模板题,难度大一点会结合其他知识点考察&#x…...
122.(leaflet篇)leaflet地图图片之间存在缝隙
听老人家说:多看美女会长寿 地图之家总目录(订阅之前建议先查看该博客) 文章末尾处提供保证可运行完整代码包,运行如有问题,可“私信”博主。 存在缝隙–效果如下所示: 解决缝隙–效果如下所示: 下面献上完整代码,代码重要位置会做相应解释 <!DOCTYPE html>…...
4.类的基本概念
目录 4.1 类的概述 类是一种活动的数据结构 4.2 程序和类:一个快速实例 4.3 声明类 4.4 类成员 4.4.1 字段 1.显示和隐式字段初始化 2. 声明多个字段 4.4.2 方法 4.5 创建变量和类的实例 4.6 为数据分配内存 合并这两个步骤 4.7 实例成员 4.8 访问修饰…...
有图解有案例,我终于把 Condition 的原理讲透彻了
哈喽大家好,我是阿Q! 20张图图解ReentrantLock加锁解锁原理文章一发,便引发了大家激烈的讨论,更有小伙伴前来弹窗:平时加解锁都是直接使用Synchronized关键字来实现的,简单好用,为啥还要引用Re…...
Linux之找回root密码
文章目录前言一、启动系统二、进入编辑界面三、修改密码前言 当我们使用root用户登陆Linux时,忘记了登陆密码,改怎样修改登陆密码呢,接下来将介绍如何修改root密码 一、启动系统 首先,启动系统,进入开机界面&#x…...
stack_queue | priority_queue | 仿函数
文章目录1. stack 的使用2. stack的模拟实现3. queue的使用4. queue的模拟实现5. deque ——双端队列deque优缺点6. priority_queue ——优先级队列1. priority_queue的使用2. priority_queue的模拟实现push——插入pop ——删除top —— 堆顶仿函数问题完整代码实现1. stack 的…...
第十四届蓝桥杯三月真题刷题训练——第 14 天
目录 第 1 题:组队 题目描述 运行限制 代码: 第 2 题:不同子串 题目描述 运行限制 代码: 思路: 第 3 题:等差数列 题目描述 输入描述 输出描述 输入输出样例 运行限制 代码: 思…...
【Hadoop-yarn-01】大白话讲讲资源调度器YARN,原来这么好理解
YARN作为Hadoop集群的御用调度器,在整个集群的资源管理上立下了汗马功劳。今天我们用大白话聊聊YARN存在意义。 有了机器就有了资源,有了资源就有了调度。举2个很鲜活的场景: 在单台机器上,你开了3个程序,分别是A、B…...
技术掉:PDF显示,使用pdf.js
PDF 显示 场景: 其实直接显示 pdf 可以用 iframe 标签,但产品觉得浏览器自带的 pdf 预览太丑了,而且无法去除那些操作栏。 解决方案:使用 pdf.js 进行显示 第一步:引入 pdf.js 去官网下载稳定版的 pdf.js 文件 然后…...
有关pytorch的一些总结
Tensor 含义 张量(Tensor):是一个多维数组,它是标量、向量、矩阵的高维拓展。 创建 非随机创建 1.用数组创建 将数组转化为tensor np.ones([a,b]) 全为1 #首先导入PyTorch import torch#数组创建 import numpy as np anp.arr…...
基础IO【Linux】
文章目录:文件相关知识C语言文件IOstdin & stdout & stderr系统文件 IOopenclosewriteread文件描述符文件描述符的分配规则重定向dup2系统调用FILEFILE中的文件描述符FILE中的缓冲区理解文件相关知识 文件 文件内容 文件属性(每一个已经存在的…...
Vue3——自定义封装上传图片样式
自定义封装上传图片样式 一、首先需要新建一个自组建完善基础的结构,我这里起名为ImgUpload.vue <el-upload name"file" :show-file-list"false" accept".png,.PNG,.jpg,.JPG,.jpeg,.JPEG,.gif,.GIF,.bmp,.BMP" :multiple"…...
ChatGLM-6B (介绍以及本地部署)
中文ChatGPT平替——ChatGLM-6BChatGLM-6B简介官方实例本地部署1.下载代码2.通过conda创建虚拟环境3.修改代码4.模型量化5.详细代码调用示例ChatGLM-6B 简介 ChatGLM-6B 是一个开源的、支持中英双语问答的对话语言模型,基于 General Language Model (GLM) 架构&…...
react的基础使用
react中为什么使用jsxReact 认为渲染逻辑本质上与其他 UI 逻辑内在耦合,比如,在 UI 中需要绑定处理事件、在某些时刻状态发生变化时需要通知到 UI,以及需要在 UI 中展示准备好的数据。react认为将业务代码和数据以及事件等等 需要和UI高度耦合…...
letcode 4.寻找两个正序数组的中位数(官方题解笔记)
题目描述 给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。 算法的时间复杂度应该为 O(log (mn)) 。 1.二分查找 1.1思路 时间复杂度:O(log(mn)) 空间复杂度:O(1) 给定…...
【面试题系列】K8S常见面试题
目录 序言 问题 1. 简单说一下k8s集群内外网络如何互通的吧 2.描述一下pod的创建过程 3. 描述一下k8s pod的终止过程 4.Kubernetes 中的自动伸缩有哪些方式? 5.Kubernetes 中的故障检测有哪些方式? 6.Kubernetes 中的资源调度有哪些方式ÿ…...
字符函数和字符串函数(上)-C语言详解
CSDN的各位友友们你们好,今天千泽为大家带来的是C语言中字符函数和字符串函数的详解,掌握了这些内容能够让我们更加灵活的运用字符串,接下来让我们一起走进今天的内容吧!写这篇文章需要在cplusplus.com上大量截图,十分不易!如果对您有帮助的话希望能够得到您的支持和帮助,我会持…...
全连接神经网络
目录 1.全连接神经网络简介 2.MLP分类模型 2.1 数据准备与探索 2.2 搭建网络并可视化 2.3 使用未预处理的数据训练模型 2.4 使用预处理后的数据进行模型训练 3. MLP回归模型 3.1 数据准备 3.2 搭建回归预测网络 1.全连接神经网络简介 全连接神经网络(Multi-Layer Percep…...
html5 网站模板/网页设计模板素材图片
前言HashMap的储存是没有顺序的,而是按照key的HashCode实现.key手机品牌,value价格,这里以这个例子实现按名称排序和按价格排序.Map phonenew HashMap();phone.put("Apple",7299);phone.put("SAMSUNG",6000);phone.put("Meizu",2698);phone.put(…...
番禺网页设计公司/厦门seo代理商
1,请问一个完整的操作系统,应该包含哪些部分?答:两部分:(1)核心与其提供的接口工具。(2)利用核心提供的接口工具所开发的软件。2,UNIX 是谁写出来的?GNU计划是…...
珠海市网站建设的公司/制作网站代码
距离2018年高考还有不到一个月的时间了,很多人在准备最后冲刺的同时,也在关心高考成绩。2018各地区高考成绩排名查询,高考各高中成绩喜报榜单尚未公布,下面是往年各地区高考成绩排名查询,高考各高中成绩喜报榜单,想要了解同学可以…...
宁波建设工程主管部门网站/百度一下你就知道移动官网
开发需求 删除列表内数据,不影响到其他模块内的内容 代码实现 后端处理代码 Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {req.setCharacterEncoding("utf-8");resp.setCh…...
wordpress option/东莞seo网站排名优化
项目简述:基于开源Hadoop2.0架构的集群网络,进行海量数据的分布式计算。由于Hadoop集群规模不断扩大,而搭建一个同等规模的测试集群需要一笔昂贵的开销。目前有100台左右物料,期望预测计算节点1500的集群网络性能,目前…...
wordpress粘贴图片/电商从零基础怎么学
众所周知,Objective-C是一门面向对象的语言,一般情况下,我们在Objective-C中定义一个类时,总要提供一个初始化方法,一般大家都是这样写的: - (MyClass *)init { self [super init]; if (self) { //执行一…...