2024-08-23



package main
 
import (
    "fmt"
    "sync"
    "sync/atomic"
    "time"
)
 
// 使用互斥锁实现计数器
type CounterL struct {
    count int
    lock  sync.Mutex
}
 
func (c *CounterL) Increment() {
    c.lock.Lock()
    defer c.lock.Unlock()
    c.count++
}
 
func (c *CounterL) Value() int {
    c.lock.Lock()
    defer c.lock.Unlock()
    return c.count
}
 
// 使用原子操作实现计数器
type CounterA struct {
    count int
}
 
func (c *CounterA) Increment() {
    atomic.AddInt32(&c.count, 1)
}
 
func (c *CounterA) Value() int32 {
    return atomic.LoadInt32(&c.count)
}
 
func main() {
    const goroutines = 10
    var wg sync.WaitGroup
    wg.Add(2)
 
    // 测试互斥锁
    start := time.Now()
    var cl CounterL
    for i := 0; i < goroutines; i++ {
        go func() {
            for count := 0; count < 100; count++ {
                cl.Increment()
            }
            wg.Done()
        }()
    }
    wg.Wait()
    elapsed := time.Since(start)
    fmt.Printf("Mutex counter after %d goroutines: %d operations took %s\n", goroutines, cl.Value(), elapsed)
 
    // 测试原子操作
    start = time.Now()
    var ca CounterA
    for i := 0; i < goroutines; i++ {
        go func() {
            for count := 0; count < 100; count++ {
                ca.Increment()
            }
            wg.Done()
        }()
    }
    wg.Wait()
    elapsed = time.Since(start)
    fmt.Printf("Atomic counter after %d goroutines: %d operations took %s\n", goroutines, ca.Value(), elapsed)
}

这段代码定义了两种计数器:一种使用互斥锁来保护共享资源,另一种使用原子操作来同步并发访问。然后在主函数中,分别对每种计数器进行了性能测试,通过启动多个goroutines来进行计数操作,并记录运行时间。这样可以清晰地展示互斥锁和原子操作的应用场景和性能差异。

2024-08-23

报错解释:

这个错误表明你正在尝试运行go命令行工具的mod子命令,但是该版本的Go语言环境不识别这个子命令。go mod是Go语言中用于管理依赖项的工具,在Go 1.11版本及以上才引入。

解决方法:

  1. 确认你的Go语言版本至少为1.11。可以通过运行go version来查看当前版本。
  2. 如果你的版本低于1.11,需要升级Go语言到一个较新的版本。可以从Go官网(https://golang.org/dl/)下载最新的安装包进行安装或者使用包管理工具进行升级。
  3. 如果你已经是1.11或更高版本,但仍然遇到这个问题,可能是因为GO111MODULE环境变量没有设置正确。可以通过设置GO111MODULE=on来启用模块支持。
  4. 在某些情况下,你可能需要删除$GOPATH/src/mod$GOPATH/bin目录下的内容,然后重新运行go mod命令。

请根据你的实际情况选择适当的解决方法。

2024-08-23



package uuid
 
import (
    "crypto/sha1"
    "encoding/binary"
    "encoding/hex"
)
 
// 生成基于 SHA1 和随机性的 UUID version 7
func NewV7() UUID {
    // 使用 sha1 哈希函数和随机性生成字节序列
    hash := sha1.New()
    randomness.Write(hash) // randomness 是一个 io.Writer 接口,提供随机性
 
    // 读取哈希的前 16 个字节
    var b [16]byte
    hash.Sum(b[:0])
 
    // 设置版本号为 7
    b[6] = (b[6] & 0x0f) | 0x70
 
    // 设置变体为非标准变体
    b[8] = (b[8] & 0x3f) | 0x80
 
    return UUID{b}
}
 
// 打印 UUID
func (u UUID) String() string {
    return hex.EncodeToString(u[:])
}

这个代码示例提供了一个简化版本的 NewV7 函数,用于生成 UUID version 7。它使用了 Go 语言标准库中的 crypto/sha1 包和 encoding/hex 包。这个函数首先创建了一个 sha1.New() 哈希对象,然后通过写入随机性来生成一个哈希序列。最后,通过设置 UUID 的版本号和变体,完成了 UUID version 7 的生成。UUID 结构体的实现在这里没有给出,但它应该是一个存储 16 字节 UUID 的数组或结构体。String 方法用于将 UUID 转换为其十六进制字符串表示形式。

2024-08-23



# 使用官方 Golang 镜像作为基础镜像
FROM golang:1.16-alpine
 
# 设置工作目录
WORKDIR /go/src/app
 
# 将当前目录下的所有文件复制到容器的工作目录
COPY . .
 
# 设置环境变量,指定应用的端口
ENV PORT=8080
 
# 编译 Go 程序,生成一个可执行文件
RUN go build -o myapp
 
# 容器启动时执行的命令,运行编译好的应用
CMD ["/go/src/app/myapp"]

这个Dockerfile定义了一个用于Go语言应用的轻量级Docker镜像。它使用了官方的Go镜像作为基础,设置了工作目录,复制了应用代码,编译了应用,并且指定了容器启动时运行编译好的应用的命令。这个例子展示了如何使用Docker来快速部署和运行Go语言应用,并且保持了镜像的轻量化。

2024-08-23

在Ubuntu 20.04上使用VS Code搭建Go开发环境的步骤如下:

  1. 安装Go语言:



sudo apt update
sudo apt install golang-go
  1. 设置Go代理(如果需要):



go env -w GOPROXY=https://goproxy.io,direct
  1. 安装VS Code:



wget -qO- https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor -o microsoft.gpg
sudo mv microsoft.gpg /etc/apt/trusted.gpg.d/microsoft.gpg
sudo sh -c 'echo "deb [arch=amd64] https://packages.microsoft.com/repos/code stable main" > /etc/apt/sources.list.d/vscode.list'
sudo apt install code
  1. 在VS Code中安装Go插件:
  • 打开VS Code
  • Ctrl+P 输入 ext install
  • 搜索并安装 Go 插件
  1. 配置VS Code的设置(可选):
  • Ctrl+, 打开设置
  • 添加以下配置,如果你的Go环境不在默认位置:



{
  "go.goroot": "/usr/lib/go-1.13",
  "go.gopath": "/home/yourusername/go"
}

完成以上步骤后,你应该已经在Ubuntu 20.04上配置好了Go的开发环境。

2024-08-23



// 并发地读取两个channel
func readFromChannels(c1, c2 <-chan string) {
    for {
        select {
        case msg1 := <-c1:
            fmt.Println("Received from c1:", msg1)
        case msg2 := <-c2:
            fmt.Println("Received from c2:", msg2)
        }
    }
}
 
func main() {
    // 创建两个channel
    c1 := make(chan string)
    c2 := make(chan string)
 
    // 创建goroutine去写入channel
    go func() {
        c1 <- "Hello"
        c1 <- "World"
        close(c1)
    }()
 
    go func() {
        c2 <- "Golang"
        c2 <- "Concurrency"
        close(c2)
    }()
 
    // 并发地从两个channel读取数据
    readFromChannels(c1, c2)
}

这段代码展示了如何使用Go语言的channel和goroutine来实现并发地从两个channel中读取数据。readFromChannels函数通过select关键字并发地监听两个channel,使得在多个channel进行选择时,可以公平地处理发送给它们的数据。这是Go语言中实现并发编程的一个常见模式。

2024-08-23



package main
 
import (
    "fmt"
    "github.com/gin-gonic/gin"
)
 
func main() {
    // 初始化一个Gin引擎
    r := gin.Default()
 
    // 设置一个GET路由,当访问/时执行
    r.GET("/", func(c *gin.Context) {
        c.JSON(200, gin.H{
            "message": "Hello, world!",
        })
    })
 
    // 启动服务器,在端口8080监听
    // 如果你使用的是Go Modules,Go会自动处理依赖关系
    r.Run(":8080")
}

这段代码演示了如何使用Golang创建一个简单的Web服务器,并使用Gin Web框架来处理HTTP请求。在这个例子中,我们定义了一个路由处理函数来响应GET请求,并在端口8080上启动服务器。这里使用了Go Modules来管理依赖,这是Go语言推荐的包管理方式。

2024-08-23



package main
 
import (
    "fmt"
    "math/big"
)
 
func main() {
    // 创建一个big.Int实例并初始化为10000
    n := big.NewInt(10000)
 
    // 创建另一个big.Int实例并初始化为20000
    m := big.NewInt(20000)
 
    // 计算两个数的和
    sum := new(big.Int)
    sum.Add(n, m) // 等价于 sum = n + m
 
    // 计算两个数的差
    diff := new(big.Int)
    diff.Sub(n, m) // 等价于 diff = n - m
 
    // 打印结果
    fmt.Printf("Sum: %s\n", sum.String())
    fmt.Printf("Difference: %s\n", diff.String())
}

这段代码演示了如何使用Go语言的math/big包来处理大整数的加法和减法。代码中创建了两个big.Int类型的数字,并计算了它们的和与差。最后,打印出了结果。这个例子简单且直接地展示了math/big包的基本用法。

2024-08-23

在Golang中,标识符是用来命名变量、常量、类型、接口、方法等的名字。它必须以字母或下划线开始,后面可以跟任意数量的字母、数字或下划线。

关于Golang的标识符,有以下几点需要注意:

  1. 标识符中不能包含空格、符号、保留字等。
  2. 标识符不能以数字开头。
  3. 标识符是区分大小写的。
  4. 不能使用Golang的预定义的全部关键字作为标识符。

Golang的25个关键字如下:

  • break
  • default
  • func
  • interface
  • select
  • case
  • defer
  • go
  • map
  • struct
  • chan
  • else
  • goto
  • package
  • switch
  • const
  • fallthrough
  • if
  • range
  • type
  • continue
  • for
  • import
  • return
  • var

Golang的36个预定义的字符包括:

  • '(单引号)
  • )(单引号)
  • *(星号)
  • +(加号)
  • ,(逗号)
  • -(连字符)
  • .(点)
  • /(斜杠)
  • :(冒号)
  • ;(分号)
  • <(小于)
  • =(等于)
  • (大于)
  • ?(问号)
  • @(at符号)
  • [(左方括号])
  • ](右方括号)
  • ^(caret符号)
  • {(左大括号)
  • |(竖线)
  • }(右大括号)
  • \_(下划线)

以上就是Golang中关于标识符、关键字和预定义字符的基本知识。

2024-08-23

在 Golang 中操作 JSON 时,可能会遇到一些常见的问题和陷阱。以下是一些需要注意的点:

  1. 使用 encoding/json 包。
  2. 使用 map[string]interface{} 处理动态 JSON。
  3. 使用指针来修改结构体字段的值。
  4. 正确处理 JSON 数组,可以用 []interface{} 或者自定义 slice 类型。
  5. 确保 JSON 字段的首字母为大写以便于导出(在其他包中使用)。
  6. 使用 json.Marshaljson.Unmarshal 进行序列化和反序列化。
  7. 处理 JSON 解码错误。

示例代码:




package main
 
import (
    "encoding/json"
    "fmt"
)
 
func main() {
    // JSON 解码示例
    jsonData := `{"name":"John", "age":30}`
    var result map[string]interface{}
    err := json.Unmarshal([]byte(jsonData), &result)
    if err != nil {
        panic(err)
    }
    fmt.Println(result)
 
    // JSON 编码示例
    name := "Jane"
    age := 25
    data, err := json.Marshal(map[string]interface{}{
        "name": name,
        "age":  age,
    })
    if err != nil {
        panic(err)
    }
    fmt.Println(string(data))
}

在这个例子中,我们展示了如何进行 JSON 的解码和编码。注意错误处理是必要的,因为 JSON 操作可能会因格式错误、字段不存在等原因失败。