首页 » 网站推广 » phpcssfmt技巧_站长带你分析println 和 fmtPrintln 的诡异问题

phpcssfmt技巧_站长带你分析println 和 fmtPrintln 的诡异问题

访客 2024-11-19 0

扫一扫用手机浏览

文章目录 [+]

packagemainimport("fmt")funcmain(){done:=make(chanint)gofunc(){println("你好")done<-1}()m:=<-donefmt.Println(m)}

在线运行:https://play.studygolang.com/p/Itvy83BKIIZ (细心的你会创造,“你好”是赤色的,“1”是玄色)

他给出的结果是:有时候 “你好” 先输出,有时候 1 先输出,顺序不定。

phpcssfmt技巧_站长带你分析println 和 fmtPrintln 的诡异问题

结果群里其他人一试验,创造顺序一贯是固定的,即:

phpcssfmt技巧_站长带你分析println 和 fmtPrintln 的诡异问题
(图片来自网络侵删)

你好

1

原来他利用的是 Goland,如果在终端实行,顺序并不会随机,如果你有 Goland,可以试试。

接着大家谈论,引出了一个小知识点。

println 和 fmt.Println 的差异

println 是 builtin 包供应,措辞内置,而 fmt.Println 来自标准库。
从 println 函数的注释中还可以理解到更多的信息:

//Theprintlnbuilt-infunctionformatsitsargumentsinan//implementation-specificwayandwritestheresulttostandarderror.//Spacesarealwaysaddedbetweenargumentsandanewlineisappended.//Printlnisusefulforbootstrappinganddebugging;itisnotguaranteed//tostayinthelanguage.

fmt.Println 输出到标准输出(os.Stdout),而 println 输出至标准缺点(os.Stderr)。
而且 println 在参数和换行都会追加空格。

这里要强调一点:println 紧张用于程序启动和调试,措辞内部实现紧张用它。
但并不担保未来会一贯有这个函数。
以是,建议还是老诚笃实用 fmt 中的打印函数吧。

引用 go101 中的一个问答:内置的`print`和`println`函数与`fmt`和`log`标准库包中相应的打印函数有什么差异?[1]

内置的print/println函数总是写入标准缺点。
fmt标准包里的打印函数总是写入标准输出。
log标准包里的打印函数会默认写入标准缺点,然而也可以通过log.SetOutput函数来配置。
内置print/println函数的调用不能接管数组和构造体参数。
对付组合类型的参数,内置的print/println函数将输出参数的底层值部的地址,而fmt和log标准库包中的打印函数将输出参数的字面值。
目前(Go 1.14),对付标准编译器,调用内置的print/println函数不会使调用参数引用的值逃逸到堆上,而fmt和log标准库包中的的打印函数将使调用参数引用的值逃逸到堆上。
如果一个实参有String() string或Error() string方法,那么fmt和log标准库包里的打印函数在打印参数时会调用这两个方法,而内置的print/println函数则会忽略参数的这些方法。
内置的print/println函数不担保在未来的Go版本中连续存在。
缓冲问题

由于涉及到不同的输出终端,自然涉及到一个问题:缓冲。
对 C 有所理解的都知道,基于流的 I/O 供应了 3 种缓冲:

全缓冲:直到缓冲区被填满,才调用系统 I/O 函数。
对付读操作来说,直到读入的内容的字节数即是缓冲区大小或者文件已经到达结尾,才进行实际的 I/O 操作,将外存文件内容读入缓冲区;对付写操作来说,直到缓冲区被填满,才进行实际的 I/O 操作,缓冲区内容写到外存文件中。
磁盘文件常日是全缓冲的。
行缓冲:直到碰着换行符 ‘\n’,才调用系统 I/O 库函数。
对付读操作来说,碰着换行符 ‘\n’ 才进行 I/O 操作,将所读内容读入缓冲区;对付写操作来说,碰着换行符 ‘\n’ 才进行 I/O 操作,将缓冲区内容写到外存中。
由于缓冲区的大小是有限的,以是当缓冲区被填满时,纵然没有碰着换行符‘\n’,也同样会进行实际的 I/O 操作。
标准输入 stdin 和标准输出 stdout 默认都是行缓冲的。
无缓冲:没有缓冲区,数据会立即读入或者输出到外存文件和设备上。
标准缺点 stderr 是无缓冲的,这样担保缺点提示和输出能够及时反馈给用户,供用户打消缺点。

可见,C 中基于流的 I/O,标准输出(stdout)和标准缺点(stderr)缓冲办法是不一样的。

如果 Go 中也是如此,那么我们可以改造上面的代码进行试验(改换 println 和 fmt.Println 的位置,以及利用 print 和 fmt.Print),你会创造,在终端输出顺序永久是固定的。
也便是说,Go 中的 os.Stdout 并不是行缓冲的。
在 golang-nuts 谈论组中有人问了这个问题:os.Stdout is not buffered ?[2],官方给的回答是,os.Stdout 是无缓冲的,由于它的类型实际上是 os.File,很显然 os.File 是无缓冲的。

既然 os.Stdout 和 os.Stderr 都是无缓冲的,那么终真个输出顺序是固定的也就不足为奇了。
我们自己实现一个行缓冲的 os.Stdout,来验证它。

packagemainimport("bufio""bytes""fmt""io""os")funcmain(){writer:=NewLineBufferedWriter(os.Stdout)deferwriter.Flush()done:=make(chanint)gofunc(){fmt.Fprint(writer,"你好")done<-1}()m:=<-doneprintln(m)}typeLineBufferedWriterstruct{bufio.Writer}funcNewLineBufferedWriter(wio.Writer)LineBufferedWriter{return&LineBufferedWriter{Writer:bufio.NewWriter(w),}}func(wLineBufferedWriter)Write(p[]byte)(nint,errerror){n,err=w.Writer.Write(p)iferr!=nil{returnn,err}ifbytes.Contains(p,[]byte{'\n'}){w.Flush()}returnn,err}

以上代码,由于有行缓冲,永久输出:

1

你好

你扯吧?我这里怎么还是顺序不愿定。

好吧,你依然用 Goland 运行的吧。

总结

通过以上的剖析,我们可以得出如下结论:

Go 措辞中的 标准输入、标准输出和标准缺点 都是无缓冲的。
这点和 C 措辞不一样;当你碰着“诡异”问题时,通过终端办法验证下,很多时候可能编辑器,特殊是 IDE 做了一些隐蔽的事情。
从上面的剖析可以肯定,Goland 在处理标准输出和标准缺点时做了额外的一些事情,详细是什么不得而知;Go 中如果须要缓冲,请利用 bufio 包,分外的需求,可以基于它进行扩展。
参考资料

[1]

内置的print和println函数与fmt和log标准库包中相应的打印函数有什么差异?: http://go101.studygolang.com/article/unofficial-faq.html#fmt-print-println

[2]

os.Stdout is not buffered ?: https://groups.google.com/forum/#!searchin/golang-nuts/fmt.Print$20buffer%7Csort:date/golang-nuts/PiR4P0joKeo/WM-e2FSaxGcJ

标签:

相关文章

php常量率低技巧_PHP 常量详解教程

PHP 常量常量是单个值的标识符(名称)。在脚本中无法改变该值。有效的常量名以字符或下划线开头(常量名称前面没有 $ 符号)。注释...

网站推广 2024-12-19 阅读0 评论0