跟着实例学Go语言(四)

学习Go语言,这23个网站给你整理好了 学习Go语言的教程网址 阅读详情

本教程全面涵盖了Go语言基础的各个方面。一共80个例子,每个例子对应一个语言特性点,既适合新人快速上手,也适合工作中遇到问题速查知识点。
教程代码示例来自go by example,文字部分来自本人自己的理解。

本文是教程系列的第四部分,共计20个例子、约1.5万字。

系列文章快速跳转:
跟着实例学Go语言(一)
跟着实例学Go语言(二)
跟着实例学Go语言(三)
跟着实例学Go语言(四)

61. Base64 Encoding

Go提供了Base64编解码的功能。Base64是将二进制数据转换成可读字符串的编码方式。例如我们用记事本打开jpg文件,会看到一串乱码,这个就是Base64转换后的字符串。Base64提供了64种不同的字符,6位编码表示一个字符。

package main

import (
    b64 "encoding/base64"
    "fmt"
)

func main() {

    data := "abc123!?$*&()'-=@~"
	// 将data底层的byte slice编码成字符串
    sEnc := b64.StdEncoding.EncodeToString([]byte(data))
    fmt.Println(sEnc)
	// 用Base64解码
    sDec, _ := b64.StdEncoding.DecodeString(sEnc)
    fmt.Println(string(sDec))
    fmt.Println()
	// 用URL兼容的方式编码
    uEnc := b64.URLEncoding.EncodeToString([]byte(data))
    fmt.Println(uEnc)
    // 用URL兼容的方式解码
    uDec, _ := b64.URLEncoding.DecodeString(uEnc)
    fmt.Println(string(uDec))
}
$ go run base64-encoding.go
YWJjMTIzIT8kKiYoKSctPUB+
abc123!?$*&()'-=@~

	

YWJjMTIzIT8kKiYoKSctPUB-
abc123!?$*&()'-=@~

62. Reading Files

下面例子展示了读文件的操作。

package main

import (
    "bufio"
    "fmt"
    "io"
    "os"
)

func check(e error) {
    if e != nil {
        panic(e)
    }
}

func main() {
	// 直接从文件读到byte slice
    dat, err := os.ReadFile("/tmp/dat")
    check(err)
    fmt.Print(string(dat))
	// 先打开文件,再对file做更复杂的操作
    f, err := os.Open("/tmp/dat")
    check(err)
	// 自己创建缓冲slice来接收读数据。返回的是读到的字节数
    b1 := make([]byte, 5)
    n1, err := f.Read(b1)
    check(err)
    fmt.Printf("%d bytes: %s\n", n1, string(b1[:n1]))
	// 偏移到下标为6的地方开始读取。两个参数分别代表:基于基准位置的偏移量、基准位置
    o2, err := f.Seek(6, 0)
    check(err)
    b2 := make([]byte, 2)
    n2, err := f.Read(b2)
    check(err)
    fmt.Printf("%d bytes @ %d: ", n2, o2)
    fmt.Printf("%v\n", string(b2[:n2]))

    o3, err := f.Seek(6, 0)
    check(err)
    b3 := make([]byte, 2)
    // ReadAtLeast保证一定读到指定字节数,否则返回错误
    n3, err := io.ReadAtLeast(f, b3, 2)
    check(err)
    fmt.Printf("%d bytes @ %d: %s\n", n3, o3, string(b3))

    _, err = f.Seek(0, 0)
    check(err)
	// bufio包提供了带缓冲的读写并提供一些更简便的操作,这里创建了带缓冲的reader
    r4 := bufio.NewReader(f)
  // 获取后面5个字节,但不实际移动指针,类似于队列中的peek
    b4, err := r4.Peek(5)
    check(err)
    fmt.Printf("5 bytes: %s\n", string(b4))

    f.Close()
}
$ echo "hello" > /tmp/dat
$ echo "go" >>   /tmp/dat
$ go run reading-files.go
hello
go
5 bytes: hello
2 bytes @ 6: go
2 bytes @ 6: go
5 bytes: hello

63. Writing Files

下面例子展示了写文件的操作。

package main

import (
    "bufio"
    "fmt"
    "os"
)

func check(e error) {
    if e != nil {
        panic(e)
    }
}

func main() {

    d1 := []byte("hello\ngo\n")
    // 将byte slice写入文件,若文件不存则创建,创建时使用0644代表的权限
    err := os.WriteFile("/tmp/dat1", d1, 0644)
    check(err)
	// 先创建文件,再对file做更复杂的操作
    f, err := os.Create("/tmp/dat2")
    check(err)
	// 将close操作推迟到最后
    defer f.Close()
	// 将自己创建的byte slice写入文件
    d2 := []byte{115, 111, 109, 101, 10}
    n2, err := f.Write(d2)
    check(err)
    fmt.Printf("wrote %d bytes\n", n2)
	// 直接写入一个字符串
    n3, err := f.WriteString("writes\n")
    check(err)
    fmt.Printf("wrote %d bytes\n", n3)
	// 写完后需要调用Sync()才能真正写入硬盘
    f.Sync()

	// 用bufio创建自带缓冲的writer
    w := bufio.NewWriter(f)
    n4, err := w.WriteString("buffered\n")
    check(err)
    fmt.Printf("wrote %d bytes\n", n4)
	// 写完后需要调用Flush(),将缓冲区数据落地
    w.Flush()

}
$ go run writing-files.go 
wrote 5 bytes
wrote 7 bytes
wrote 9 bytes

$ cat /tmp/dat1
hello
go
$ cat /tmp/dat2
some
writes
buffered

64. Line Filters

下面例子展示了从标准输入中读取并解析。类似于Java中的Scanner和Linux下的grep、sed命令。

package main

import (
    "bufio"
    "fmt"
    "os"
    "strings"
)

func main() {
	// 从标准输入创建scanner
    scanner := bufio.NewScanner(os.Stdin)
	
    for scanner.Scan() {
		// 默认用空格分隔,每次新读取一段
        ucl := strings.ToUpper(scanner.Text())

        fmt.Println(ucl)
    }

    if err := scanner.Err(); err != nil {
        fmt.Fprintln(os.Stderr, "error:", err)
        os.Exit(1)
    }
}
$ echo 'hello'   > /tmp/lines
$ echo 'filter' >> /tmp/lines

$ cat /tmp/lines | go run line-filters.go
HELLO
FILTER

65. File Paths

下面例子展示了对文件路径的各种操作。

package main

import (
    "fmt"
    "path/filepath"
    "strings"
)

func main() {
	// 将不同级别目录名拼接成文件的完整路径
    p := filepath.Join("dir1", "dir2", "filename")
    fmt.Println("p:", p)
	
    fmt.Println(filepath.Join("dir1//", "filename"))
    fmt.Println(filepath.Join("dir1/../dir1", "filename"))
	// 从完整路径获取目录名和文件名
    fmt.Println("Dir(p):", filepath.Dir(p))
    fmt.Println("Base(p):", filepath.Base(p))
	// 是否是绝对路径
    fmt.Println(filepath.IsAbs("dir/file"))
    fmt.Println(filepath.IsAbs("/dir/file"))
	
    filename := "config.json"
	// 获取文件扩展名
    ext := filepath.Ext(filename)
    fmt.Println(ext)
	// 获取去除扩展名后的文件名
    fmt.Println(strings.TrimSuffix(filename, ext))
	// 获取两个路径之间的相对路径
    rel, err := filepath.Rel("a/b", "a/b/t/file")
    if err != nil {
        panic(err)
    }
    fmt.Println(rel)

    rel, err = filepath.Rel("a/b", "a/c/t/file")
    if err != nil {
        panic(err)
    }
    fmt.Println(rel)
}
$ go run file-paths.go
p: dir1/dir2/filename
dir1/filename
dir1/filename
Dir(p): dir1/dir2
Base(p): filename
false
true
.json
config
t/file
../c/t/file

66. Directories

下面例子展示了对目录的相关操作。

package main

import (
    "fmt"
    "os"
    "path/filepath"
)

func check(e error) {
    if e != nil {
        panic(e)
    }
}

func main() {
	// 使用指定的名字和权限创建目录
    err := os.Mkdir("subdir", 0755)
    check(err)
	// 递归删除目录,等效于rm -rf
    defer os.RemoveAll("subdir")

    createEmptyFile := func(name string) {
        d := []byte("")
        // 通过写空数据创建文件,文件不存在就创建
        check(os.WriteFile(name, d, 0644))
    }

    createEmptyFile("subdir/file1")

	// 强制创建目录,若路径上的父级不存在则创建,等效于mkdir -p
    err = os.MkdirAll("subdir/parent/child", 0755)
    check(err)

    createEmptyFile("subdir/parent/file2")
    createEmptyFile("subdir/parent/file3")
    createEmptyFile("subdir/parent/child/file4")

	// 列出指定目录下一级的所有目录和文件
    c, err := os.ReadDir("subdir/parent")
    check(err)

    fmt.Println("Listing subdir/parent")
    for _, entry := range c {
        fmt.Println(" ", entry.Name(), entry.IsDir())
    }

	// 改变当前目录,等效于Linux下的cd命令
    err = os.Chdir("subdir/parent/child")
    check(err)
	// 对当前目录执行ReadDir
    c, err = os.ReadDir(".")
    check(err)

    fmt.Println("Listing subdir/parent/child")
    for _, entry := range c {
        fmt.Println(" ", entry.Name(), entry.IsDir())
    }

    err = os.Chdir("../../..")
    check(err)

    fmt.Println("Visiting subdir")
	// 递归遍历指定目录下的所有子目录和文件,方式为深度优先遍历
    err = filepath.Walk("subdir", visit)
}

func visit(p string, info os.FileInfo, err error) error {
    if err != nil {
        return err
    }
    fmt.Println(" ", p, info.IsDir())
    return nil
}
$ go run directories.go
Listing subdir/parent
  child true
  file2 false
  file3 false
Listing subdir/parent/child
  file4 false
Visiting subdir
  subdir true
  subdir/file1 false
  subdir/parent true
  subdir/parent/child true
  subdir/parent/child/file4 false
  subdir/parent/file2 false
  subdir/parent/file3 false

67. Temporary Files and Directories

下面例子展示了对临时文件和目录的操作。

package main

import (
    "fmt"
    "os"
    "path/filepath"
)

func check(e error) {
    if e != nil {
        panic(e)
    }
}

func main() {

    f, err := os.CreateTemp("", "sample")
    check(err)

    fmt.Println("Temp file name:", f.Name())
	// 操作系统会在程序结束后一段时间自动清理临时文件,不过最好还是自己做清理
    defer os.Remove(f.Name())

    _, err = f.Write([]byte{1, 2, 3, 4})
    check(err)

    dname, err := os.MkdirTemp("", "sampledir")
    check(err)
    fmt.Println("Temp dir name:", dname)

    defer os.RemoveAll(dname)

    fname := filepath.Join(dname, "file1")
    err = os.WriteFile(fname, []byte{1, 2}, 0666)
    check(err)
}
$ go run temporary-files-and-directories.go
Temp file name: /tmp/sample610887201
Temp dir name: /tmp/sampledir898854668

68. Embed Directive

embed编译指令可以在程序运行时从指定的文件获取数据到程序变量。这种编程风格有点类似于Srping中的依赖注入。

package main

import (
    "embed"
)

// 使用//go:embed指定从文件读取内容到变量fileString、fileByte
//go:embed folder/single_file.txt
var fileString string

//go:embed folder/single_file.txt
var fileByte []byte

// 使用//go:embed也可以打包多个文件到embed.FS类型变量,方便后续操作 
//go:embed folder/single_file.txt
//go:embed folder/*.hash
var folder embed.FS

func main() {

    print(fileString)
    print(string(fileByte))

    content1, _ := folder.ReadFile("folder/file1.hash")
    print(string(content1))

    content2, _ := folder.ReadFile("folder/file2.hash")
    print(string(content2))
}
$ mkdir -p folder
$ echo "hello go" > folder/single_file.txt
$ echo "123" > folder/file1.hash
$ echo "456" > folder/file2.hash

	

$ go run embed-directive.go
hello go
hello go
123
456

69. Testing and Benchmarking

Go对于单元测试和基准测试提供了原生的支持。仅需以指定的格式命名函数,通过正则匹配会被自动识别为单元测试或基准测试用例,再用go test命令启动测试。

package main

import (
    "fmt"
    "testing"
)

func IntMin(a, b int) int {
    if a < b {
        return a
    }
    return b
}

// 测试用例需要以Test开头
func TestIntMinBasic(t *testing.T) {
    ans := IntMin(2, -2)
    if ans != -2 {

        t.Errorf("IntMin(2, -2) = %d; want -2", ans)
    }
}

func TestIntMinTableDriven(t *testing.T) {
    var tests = []struct {
        a, b int
        want int
    }{
        {0, 1, 0},
        {1, 0, 0},
        {2, -2, -2},
        {0, -1, -1},
        {-1, 0, -1},
    }

    for _, tt := range tests {

        testname := fmt.Sprintf("%d,%d", tt.a, tt.b)
        // 用Run启动子测试用例,每个用例用数据表中获取输入
        t.Run(testname, func(t *testing.T) {
            ans := IntMin(tt.a, tt.b)
            if ans != tt.want {
                t.Errorf("got %d, want %d", ans, tt.want)
            }
        })
    }
}

// 基准测试需要以Benchmark开头
func BenchmarkIntMin(b *testing.B) {
	// b.N是执行次数,由底层自动决定,以产生稳定精确的性能统计
    for i := 0; i < b.N; i++ {
        IntMin(1, 2)
    }
}
// 执行所有测试,显示详细信息,包括子测试
$ go test -v
== RUN   TestIntMinBasic
--- PASS: TestIntMinBasic (0.00s)
=== RUN   TestIntMinTableDriven
=== RUN   TestIntMinTableDriven/0,1
=== RUN   TestIntMinTableDriven/1,0
=== RUN   TestIntMinTableDriven/2,-2
=== RUN   TestIntMinTableDriven/0,-1
=== RUN   TestIntMinTableDriven/-1,0
--- PASS: TestIntMinTableDriven (0.00s)
    --- PASS: TestIntMinTableDriven/0,1 (0.00s)
    --- PASS: TestIntMinTableDriven/1,0 (0.00s)
    --- PASS: TestIntMinTableDriven/2,-2 (0.00s)
    --- PASS: TestIntMinTableDriven/0,-1 (0.00s)
    --- PASS: TestIntMinTableDriven/-1,0 (0.00s)
PASS
ok      examples/testing-and-benchmarking    0.023s

// 执行基准测试
$ go test -bench=.
goos: darwin
goarch: arm64
pkg: examples/testing
BenchmarkIntMin-8 1000000000 0.3136 ns/op
PASS
ok      examples/testing-and-benchmarking    0.351s

70. Command-Line Arguments

通过Go提供的命令行api,可以方便地把Go编译出来的程序用作命令行工具。下面例子展示了Go程序读取命令行参数并处理的过程。

package main

import (
    "fmt"
    "os"
)

func main() {
	// os.Args包含所有参数,当前文件编译生成的可执行文件为第一个参数
    argsWithProg := os.Args
    argsWithoutProg := os.Args[1:]

    arg := os.Args[3]

    fmt.Println(argsWithProg)
    fmt.Println(argsWithoutProg)
    fmt.Println(arg)
}
$ go build command-line-arguments.go
$ ./command-line-arguments a b c d
[./command-line-arguments a b c d]       
[a b c d]
c

71. Command-Line Flags

Go支持命令行标志,为命令行提供了不同的选项。例如grep -c中的-c就是一种命令行标志。

package main

import (
    "flag"
    "fmt"
)

func main() {
	// 新定义word作为命令行标志,三个参数为:标志名、默认值、注释
    wordPtr := flag.String("word", "foo", "a string")

    numbPtr := flag.Int("numb", 42, "an int")
    forkPtr := flag.Bool("fork", false, "a bool")

    var svar string
    flag.StringVar(&svar, "svar", "bar", "a string var")

    flag.Parse()

    fmt.Println("word:", *wordPtr)
    fmt.Println("numb:", *numbPtr)
    fmt.Println("fork:", *forkPtr)
    fmt.Println("svar:", svar)
    fmt.Println("tail:", flag.Args())
}
 go build command-line-flags.go


	

$ ./command-line-flags -word=opt -numb=7 -fork -svar=flag
word: opt
numb: 7
fork: true
svar: flag
tail: []


	

$ ./command-line-flags -word=opt
word: opt
numb: 42
fork: false
svar: bar
tail: []


	

$ ./command-line-flags -word=opt a1 a2 a3
word: opt
...
tail: [a1 a2 a3]

	

$ ./command-line-flags -word=opt a1 a2 a3 -numb=7
word: opt
numb: 42
fork: false
svar: bar
tail: [a1 a2 a3 -numb=7]

$ ./command-line-flags -h
Usage of ./command-line-flags:
  -fork=false: a bool
  -numb=42: an int
  -svar="bar": a string var
  -word="foo": a string


$ ./command-line-flags -wat
flag provided but not defined: -wat
Usage of ./command-line-flags:
...

72. Command-Line Subcommands

Go支持命令行子命令。例如go build和run就是go下面的两个子命令。

package main

import (
    "flag"
    "fmt"
    "os"
)

func main() {

    fooCmd := flag.NewFlagSet("foo", flag.ExitOnError)
    fooEnable := fooCmd.Bool("enable", false, "enable")
    fooName := fooCmd.String("name", "", "name")

    barCmd := flag.NewFlagSet("bar", flag.ExitOnError)
    barLevel := barCmd.Int("level", 0, "level")

    if len(os.Args) < 2 {
        fmt.Println("expected 'foo' or 'bar' subcommands")
        os.Exit(1)
    }

    switch os.Args[1] {

    case "foo":
        fooCmd.Parse(os.Args[2:])
        fmt.Println("subcommand 'foo'")
        fmt.Println("  enable:", *fooEnable)
        fmt.Println("  name:", *fooName)
        fmt.Println("  tail:", fooCmd.Args())
    case "bar":
        barCmd.Parse(os.Args[2:])
        fmt.Println("subcommand 'bar'")
        fmt.Println("  level:", *barLevel)
        fmt.Println("  tail:", barCmd.Args())
    default:
        fmt.Println("expected 'foo' or 'bar' subcommands")
        os.Exit(1)
    }
}
$ go build command-line-subcommands.go 


	

$ ./command-line-subcommands foo -enable -name=joe a1 a2
subcommand 'foo'
  enable: true
  name: joe
  tail: [a1 a2]


	

$ ./command-line-subcommands bar -level 8 a1
subcommand 'bar'
  level: 8
  tail: [a1]


	

$ ./command-line-subcommands bar -enable a1
flag provided but not defined: -enable
Usage of bar:
  -level int
        level

73. Environment Variables

下面例子展示了环境变量的设置和读取。

package main

import (
    "fmt"
    "os"
    "strings"
)

func main() {
	// 环境变量为进程级别,进程退出后设置的变量消失
    os.Setenv("FOO", "1")
    fmt.Println("FOO:", os.Getenv("FOO"))
    fmt.Println("BAR:", os.Getenv("BAR"))

    fmt.Println()
    // 读取到的变量列表与具体机器有关
    for _, e := range os.Environ() {
        pair := strings.SplitN(e, "=", 2)
        fmt.Println(pair[0])
    }
}
$ go run environment-variables.go
FOO: 1
BAR: 


	

TERM_PROGRAM
PATH
SHELL
...
FOO


	

$ BAR=2 go run environment-variables.go
FOO: 1
BAR: 2
...

74. HTTP Client

Go提供了方便的Http请求和处理的工具。下面是HTTP客户端的例子。

package main

import (
    "bufio"
    "fmt"
    "net/http"
)

func main() {
	// 仅一句Get就能访问http url
    resp, err := http.Get("https://gobyexample.com")
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()

    fmt.Println("Response status:", resp.Status)

    scanner := bufio.NewScanner(resp.Body)
    for i := 0; scanner.Scan() && i < 5; i++ {
        fmt.Println(scanner.Text())
    }

    if err := scanner.Err(); err != nil {
        panic(err)
    }
}
$ go run http-clients.go
Response status: 200 OK
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Go by Example</title>

75. HTTP Server

下面是HTTP服务端的例子。仅需HandleFunc和ListenAndServe两句代码就能实现一个简单的服务端,非常方便。

package main

import (
    "fmt"
    "net/http"
)

func hello(w http.ResponseWriter, req *http.Request) {

    fmt.Fprintf(w, "hello\n")
}

func headers(w http.ResponseWriter, req *http.Request) {

    for name, headers := range req.Header {
        for _, h := range headers {
            fmt.Fprintf(w, "%v: %v\n", name, h)
        }
    }
}

func main() {
	
    http.HandleFunc("/hello", hello)
    http.HandleFunc("/headers", headers)

    http.ListenAndServe(":8090", nil)
}
 	

$ go run http-servers.go &

$ curl localhost:8090/hello
hello

76. Context

下面例子展示了通过HTTP请求上下文处理请求取消的操作。

package main

import (
    "fmt"
    "net/http"
    "time"
)

func hello(w http.ResponseWriter, req *http.Request) {

    ctx := req.Context()
    fmt.Println("server: hello handler started")
    defer fmt.Println("server: hello handler ended")

    select {
    case <-time.After(10 * time.Second):
        fmt.Fprintf(w, "hello\n")
    // 若在10秒内断开请求连接,则执行下面逻辑
    case <-ctx.Done():

        err := ctx.Err()
        fmt.Println("server:", err)
        internalError := http.StatusInternalServerError
        http.Error(w, err.Error(), internalError)
    }
}

func main() {

    http.HandleFunc("/hello", hello)
    http.ListenAndServe(":8090", nil)
}
$ go run context-in-http-servers.go &

$ curl localhost:8090/hello
server: hello handler started
// 这里模拟在接到回应前,断开请求连接
^C
server: context canceled
server: hello handler ended

77. Spawning Processes

下面例子展示了在Go程序中调用其他命令,如date,并获取返回值。

package main

import (
    "fmt"
    "io"
    "os/exec"
)

func main() {

    dateCmd := exec.Command("date")

    dateOut, err := dateCmd.Output()
    if err != nil {
        panic(err)
    }
    fmt.Println("> date")
    fmt.Println(string(dateOut))

    _, err = exec.Command("date", "-x").Output()
    if err != nil {
        switch e := err.(type) {
        case *exec.Error:
            fmt.Println("failed executing:", err)
        case *exec.ExitError:
            fmt.Println("command exit rc =", e.ExitCode())
        default:
            panic(err)
        }
    }

    grepCmd := exec.Command("grep", "hello")

    grepIn, _ := grepCmd.StdinPipe()
    grepOut, _ := grepCmd.StdoutPipe()
    grepCmd.Start()
    grepIn.Write([]byte("hello grep\ngoodbye grep"))
    grepIn.Close()
    grepBytes, _ := io.ReadAll(grepOut)
    grepCmd.Wait()

    fmt.Println("> grep hello")
    fmt.Println(string(grepBytes))

    lsCmd := exec.Command("bash", "-c", "ls -a -l -h")
    lsOut, err := lsCmd.Output()
    if err != nil {
        panic(err)
    }
    fmt.Println("> ls -a -l -h")
    fmt.Println(string(lsOut))
}
$ go run spawning-processes.go 
> date
Thu 05 May 2022 10:10:12 PM PDT
	

command exited with rc = 1
> grep hello
hello grep

	

> ls -a -l -h
drwxr-xr-x  4 mark 136B Oct 3 16:29 .
drwxr-xr-x 91 mark 3.0K Oct 3 12:50 ..
-rw-r--r--  1 mark 1.3K Oct 3 16:28 spawning-processes.go

78. Exec’ing Processes

下面展示了从Go程序启动一个新的进程。与上一个例子不同,这里启动新进程后,原Go程序会主动退出。

package main

import (
    "os"
    "os/exec"
    "syscall"
)

func main() {

    binary, lookErr := exec.LookPath("ls")
    if lookErr != nil {
        panic(lookErr)
    }

    args := []string{"ls", "-a", "-l", "-h"}

    env := os.Environ()

    execErr := syscall.Exec(binary, args, env)
    if execErr != nil {
        panic(execErr)
    }
}
$ go run execing-processes.go
total 16
drwxr-xr-x  4 mark 136B Oct 3 16:29 .
drwxr-xr-x 91 mark 3.0K Oct 3 12:50 ..
-rw-r--r--  1 mark 1.3K Oct 3 16:28 execing-processes.go

79. Signals

下面例子展示了信号量的使用。通过监听中止信号量,在外部强行中断程序后,还能执行一段结束代码。

package main

import (
    "fmt"
    "os"
    "os/signal"
    "syscall"
)

func main() {

    sigs := make(chan os.Signal, 1)
	// 监听程序中止的信号量,并存入通道sigs
    signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)

    done := make(chan bool, 1)

    go func() {
		// 从sigs通道读取信号量并打印
        sig := <-sigs
        fmt.Println()
        fmt.Println(sig)
        done <- true
    }()

    fmt.Println("awaiting signal")
    <-done
    fmt.Println("exiting")
}
$ go run signals.go
awaiting signal
^C
interrupt
exiting

80. Exit

下面例子展示了以指定退出码主动退出进程。主动退出时不会指定defer语句。

package main

import (
    "fmt"
    "os"
)

func main() {

    defer fmt.Println("!")

    os.Exit(3)
}
$ go run exit.go
exit status 3

$ go build exit.go
$ ./exit
$ echo $?
3
WinDbg调试工具安装配置保姆级教程:从下载到符号路径设置 程序崩溃与系统蓝屏是开发运维中常见的棘手问题,定位根因往往需要借助专业调试工具。调试器通过解析转储文件(DMP)和符号文件(PDB)将内存地址翻译为可读的函数名,这是分析崩溃现场的核心原理。在Windows环境下,微软提供的WinDbg调试器具备强大的用户态与内核态分析能力,无论是排查生产环境服务挂死,还是解读蓝屏转储,都能提供关键线索。然而,正确安装WinDbg并配置符号路径是发挥其价值的前提——符号服务器配置不当,分析结果将是一堆十六进制地址。从下载渠道选择、安装步骤到符号路径与源路径配置,本文提供一套 阅读详情

相关推荐

用Java实现森林冰火人游戏

g.drawString("游戏结束", CELL_SIZE * COLS / 2 - 100, CELL_SIZE * ROWS / 2);g.drawString("难度级别: " + difficultyLevel, 10, CELL_SIZE * ROWS + 60);g.drawString("生命值: " + playerHealth, 10, CELL_SIZE * ROWS + 20);setTitle("森林冰火人");

2401_82778412的博客 1734

Testing and Benchmarking

首先创建源码文件并实现方法。 func IntMin(a, b int) int { if a < b { return a } return b } 然后创建测试文件并实现测试方法。测试文件的文件名一般是源码文件名加_test后缀。 func TestIntMinBasic(t *testing.T) { ans := IntMin(2, -2) if ans != -2 { t.Errorf("IntMin(2, -2) = %d; want -2", ans) } } f

钻石星辰的专栏 168

机器视觉避坑指南:为什么你的Halcon划痕检测总漏检?(附优化方案)

本文深入剖析了Halcon在表面划痕检测中漏检率高的常见原因,如灰度值波动、噪声干扰及形态学参数设置不当。针对复杂背景与低对比度划痕,提出了超越官方例程的多尺度纹理分析与特征融合优化方案,旨在帮助开发者构建更鲁棒、稳定的机器视觉检测流程,有效提升检出率。

weixin_29057163的博客 234

跟着实例学Go语言(一)

本教程全面涵盖了Go语言基础的各个方面。一共80个例子,每个例子对应一个语言特性点,非常适合新人快速上手。 教程代码示例来自go by example,文字部分来自本人自己的理解。本文是教程系列的第一部分,共计20个例子、10233字。下面的例子演示了如何打印经典的“Hello world”语句,以及运行和编译go代码的方法。 2. Values 下面的代码演示了打印不同类型变量的方法。 3. Variables 下面的例子演示了定义和初始化变量的方法。Go的编译器可以做类型推导,无需显示指定类型。

长三月的游戏开发 985

LPDDR5 学习笔记01:功能概述

LPDDR5/LPDDR5X SDRAM设备采用8/16DQ通道架构,支持2Gb-32Gb容量。通过CK命令时钟和更快的WCK数据时钟协同工作,7个CA引脚传输控制信息,支持2:1或4:1的WCK:CK比例。设备提供BG/8B/16B三种存储模式选择,分别支持不同数据速率(最高8.533Gbps)和突发长度。采用0.5V VDDQ电源电压,实现高速双倍数据速率传输,并通过差分WCK时钟完成数据采样。读写采用突发访问机制,需先激活命令再执行读写操作。新型LPDDR5X仅支持BG和16B模式,优化了高速性能。

一只豌豆象的博客 2202

2024年12月中国电子学会青少年软件编程(Python)等级考试试卷(五级)答案 + 解析

2024年12月中国电子学会青少年软件编程(Python)等级考试试卷(五级)答案 + 解析

伶俐角少儿编程 484

跟着实例学Go语言(二)

本教程全面涵盖了Go语言基础的各个方面。一共80个例子,每个例子对应一个语言特性点,非常适合新人快速上手。 教程代码示例来自go by example,文字部分来自本人自己的理解。本文是教程系列的第二部分,共计20个例子、约1.2万字。下面的例子展示了用interface关键字定义接口。接口是一组方法签名的集合,用于实现多态。Go不像其他语言那样需要通过extend关键字显式指定类型的继承关系。如果一个struct类型实现了interface中定义的所有方法,那么就认为这个struct类型属于interfa

长三月的游戏开发 605

Python之achatbot包语法、参数和实际应用案例

本书是一本PyTorch深度学习实战指南,分为基础(前6章)和进阶(后5章)两大部分。基础部分涵盖张量运算、神经网络、数据预处理及CNN等核心内容;进阶部分则深入图像、文本、音频建模技术,重点解析Transformer架构及大语言模型开发。全书采用"理论+实践"模式,适合从零基础到进阶的PyTorch学习者。

王国平的博客 632

跟着实例学Go语言(三)

本教程全面涵盖了Go语言基础的各个方面。一共80个例子,每个例子对应一个语言特性点,非常适合新人快速上手。 教程代码示例来自go by example,文字部分来自本人自己的理解。本文是教程系列的第三部分,共计20个例子、约1.5万字。Go中处理同步的另一方式是使用互斥锁,这也是许多其他语言会采用的方式,保证加锁的代码块为原子操作。 42. Stateful Goroutines 比起互斥锁,Go更推荐使用通道通信的方式来实现goroutine之间的同步。以下是一个使用channel通信来实现并发

长三月的游戏开发 478

【QNX+Android虚拟化方案】136 - QNX 侧 Coredump 文件解析

【QNX+Android虚拟化方案】136 - QNX 侧 Coredump 文件解析

|~~~热爱生活、努力学习的小伙汁~~~| 1909

基于FPGA的64点fft变换verilog开发

这精度的损失在10bits看来对最后的误差的影响是存在是不可忽略的。此后,在这思想基础上又开发了高基和分裂基等快速算法,随着数字技术的高速发展,1976年出现建立在数论和多项式理论基础上的维诺格勒傅里叶变换算法(WFTA)和素因子傅里叶变换算法。采用这种算法能使计算机计算离散傅里叶变换所需要的乘法次数大为减少,特别是被变换的抽样点数N越多,FFT算法计算量的节省就越显著。采用这种算法能使计算机计算离散傅里叶变换所需要的乘法次数大为减少,特别是被变换的抽样点数N越多,FFT算法计算量的节省就越显著。

FPGA/MATLAB学习教程/源码/项目合作开发 1977

跟着deepseek学golang--认识golang

Go Modules已解决依赖管理问题,但仍有18%的遗留项目依赖vendor目录(2023年CNCF调研数据)Uber代码规范要求错误必须带堆栈上下文,需使用github.com/pkg/errors。Docker引擎采用Go编写,单个二进制文件仅38MB(v20.10.24版本)通过go mod vendor生成独立依赖目录,保证构建环境纯净。etcd:基于RAFT共识算法,实现10,000+次写操作/秒。同等并发任务下,Go比Java线程模型节省96%内存。不支持元编程,泛型方法仍受限。

认真,努力,记录学习 1420

关于DEJA_VU3D - Cesium功能集专栏说明

专栏主要目的是对工作中基于Cesium实现过的功能进行整合,暂时打算一周2-3更的样子来更新,每一篇都会奉上完整源代码(尽可能把代码简洁一些)。功能实现大多数是自己琢磨实现的,也有参考其他大神后整理实现的,初步算了算现在有差不多实现小130个左右的功能,后续也会不断的更新。..........................................

总得学点什么的博客 2万+

【Go语言入门指南】零基础入门 go 语言 | Golang 入门指南

这篇文章是我自己亲身经历这样去学go的,本人是从20年的10月份开始入坑go语言的,当时的资料真的少,现在关于go语言的资料已经很多了。我就在把我自己的学习路线稍微梳理一遍,我个人觉得,学完之后,像深信服,b站,得物什么的应该是没什么问题,但如果是字节,腾讯,阿里这种就除了一些硬性条件之外,自己再刷多点题,背多点八股了。

面向生活编程 1万+

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

Golang极速入门课四,变量与常量

Golang的变量相较于其他语言的 int a 改为了 var a int,且通过语言来警告未使用的变量,将自己所存在的变量都将是存在引用的。既便于垃圾回收的查询与判断,也减少了未使用的变量挤占内存的情况。通过简短形式,使用 := 赋值操作符,也简便工作中不断声明变量的繁琐工作,当一个返回使用到多个变量时不必将这些变量单独进行声明。

猫轻王的博客 2077

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

【Go语言入门教程】Go语言简介

文章目录Go语言简介Go语言创始人1) Ken Thompson2) Rob Pike3) Robert GriesemerGo 是编译型语言为什么要学习Go语言Go语言吉祥物Go语言的特性有哪些?语法简单并发模型内存分配垃圾回收静态链接标准库工具链Go语言为并发而生哪些项目使用Go语言开发?1) [Docker](http://c.biancheng.net/docker/)2) Go语言3) Kubernetes4) etcd5) beego6) martini7) codis8) delve哪些大公司

qq_41854911的博客 2万+

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

跟着deepseek学golang--Go vs Java vs JavaScript三语言的差异

Go/Java在编译期拦截类型错误,JavaScript允许运行时自由转换。Java 14+引入Optional类型,Go推荐通过错误处理避免空指针。保证资源释放,Java需要显式。-Go:强在并发处理和部署效率。

认真,努力,记录学习 1323

【跟着我们学Golang】Go语言全平台安装

2019独角兽企业重金招聘Python工程师标准>>> ...

weixin_34417183的博客 111

【跟着我们学Golang】之面向对象

万物皆对象。学过Java编程的都知道Java是一门面向对象的语言,它拥有封装、继承和多态的特性。那可不可以说,拥有封装、继承和多态这一特性的语言就是面向对象的语言呢? 仔细想来,也确实是这样的,因为封装、继承和多态这三个特征,并不是Java语言的特征,而是面向对象的三大特征。 总结来看,所有包含封装、继承和多态者三大特征的语言都可以说是面向对象的语言。 那么Go语言是否是一门面向对象的语言呢?下面...

架构师专栏 611

从0开始Go语言,用Golang搭建网站

实践是最好的学习方式 零基础通过开发Web服务学习Go语言 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9jb019j4-1638772166928)(https://studygolang.com/img/bVbswsd?w=910&h=892)] 本文适合有一定编程基础,但是没有Go语言基础的同学。 也就是俗称的“骗你”学Go语言系列。 这是一个适合阅读的系列,我希望您能够在车上、厕所、餐厅都阅读它,涉及代码的部分也是精简而实用的。 学习需要动机 Go语言能干什

m0_46426259的博客 4488

开篇词 | 跟着学,你也能成为Go语言高手

你好,我是郝林。今天想跟你聊聊我和Go语言的故事。Go语言是由Google出品的一门通用型计算机编程语言。作为在近年来快速崛起的编程语言,Go已经成功跻身主流编程语言的行列。它的种种亮点都受到了广大编程爱好者的追捧。特别是一些对团队协作有较高要求的公司和技术团队,已经在有意识地大量使用Go语言编程,并且,使用的人群还在持续迅猛增长。我个人很喜欢Go语言。我是从2012年底开始关注Go语言的,虽然这...

优快云学习 398

Go语言与Java语言对比

宏观看Go与Java的差异 接着,我们来看一下Go语言与Java的差异之处: ``` *.无虚拟机,不跨平台(这里的平台指操作系统)(可以运行多个平台,每个平台打不同的二进制程序包),需要打包编译成对应服务器操作系统版本(windows/linux)的可执行程序(比如windows是exe)。(注:说go跨平台的是指32位和64位相同操作系统之间的跨平台) ​ *.因为Go程序直接打包成操作系统可执行的文件,没有虚拟机在中间转换的一层,所以理论上执行效率会更高(理论上更高,实际情况需具

Add小兵的博客 6871

【跟着我们学Golang】之异常处理

Java中的异常分为Error和Exception来处理,这里也以错误和异常两种,来分别讲一讲Go的异常处理。 Go 语言没有类似 Java 或 .NET 中的异常处理机制,虽然可以使用 defer、panic、recover 模拟,但官方并不主张这样做。Go 语言的设计者认为其他语言的异常机制已被过度使用,上层逻辑需要为函数发生的异常付出太多的资源。同时,如果函数使用者觉得错误处理很麻烦而忽略...

架构师专栏 968

跟着官网教程来学golang

golang learn 教程地址:https://golang.org/doc/tutorial/getting-started install 1.官网下载,go安装包; 2.配置 $GOPATH 环境变量。该目录是下载go依赖包的目录。 hello go 初始化模块 go mod init example.com/hello 新建 hello.go package main import "fmt" func main(){ fmt.PrintLn("Hello golang!") }

逆水行舟,不进则退 510

学习Go语言,这20个网站给你整理好了

作者 | 写代码的明哥来源 | Go编程时光(ID: HelloGoCode)平时为了保证自己写的文章的出错率达到最低,我需要不断的 Google 查阅资料,也因此收集了不少学习 Go ...

AI科技大本营 1万+
上一篇: 跟着实例学Go语言(三)
下一篇: 5天带你读完《Effective Java》(一)
长三月
博客等级 码龄9年 228粉丝 80原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值