Golang中这段代码有什么问题2

Golang中这段代码有什么问题2 https://play.golang.org/p/29i2_pBlgti

13 回复

谢谢!

更多关于Golang中这段代码有什么问题2的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


我讨厌时间不够用!

“arguments” 在这里的定义是什么?我尝试查阅了 fmt 包、语言规范以及 go doc,但不知道在哪里能找到它。

谢谢!这样解释就明白了。我之前学过 %d 表示十进制数,但"需要输出两个变量所以需要两个占位符"这一点我没想到。

哦,它还在那里。 在理解了参数的概念后,我重新查看了你之前的回复,终于明白了!真有趣!

有趣的参数定义。我想我明白了。

我认为在第一个问题中已经有人建议您阅读 https://golang.org/pkg/fmt/ 文档 🙂

简而言之,您需要输出两个变量,因此需要为它们准备两个正确类型的占位符。

我不小心删除了你回答中我想评论的那部分。就是你写的那段显示"Hello Cherolyn…"的代码。那段代码很有趣。

很酷,这样确实可行。只是好奇,你为什么写了两次 %d?

参数是传递给函数作为输入的内容。通过一些示例可以最清楚地说明这一点。

fmt.Println("hello")

Println() 函数接收一个参数,即值为 "hello" 的字符串。

sum = add(3,2)

add() 函数有两个参数,分别是整数 32

sum = add(3.67,two)

这里仍然有两个参数,但第一个是浮点数(3.67),第二个是变量(two)。

max_value = max(a,b,f(c),"name",'$')

max() 函数有五个参数:变量 ab,函数 f() 的返回值,一个字符串("name"),以及一个字符(美元符号字符)。

是的,一定要尝试阅读 fmt 包的文档,看看是否能理解它。文档开头介绍了 %d 和其他所谓的"动词",你可以在 fmt.Printf()(及类似)函数中使用它们。这些对于进行基本的输入输出非常有用!

fmt.Printf() 处理参数的方式大致如下:

fmt.Printf("format string", arg1, arg2, arg3 … ) // (非实际 Go 代码。无法编译。)

必须有一个格式字符串,然后可以没有额外参数,也可以有多个参数。格式字符串中的动词指示如何打印内容,而参数则提供要打印的内容。以下是一些简单示例:

fmt.Printf("hello, world\n")    /// 打印 "hello, world" 和一个换行符。

在这个例子中,格式字符串中没有动词,也没有额外参数。你也可以使用 fmt.Println("hello, world") 实现同样的效果。

var name string = "Cherolyn"

fmt.Printf("Hello, %s\n", name)    // 打印 "Hello, Cherolyn" 和一个换行符

注意 %s(用于打印字符串的动词)是如何被 name 的值替换的。

动词以百分号(%)开头,是格式化指令。对于格式字符串中的每个动词,你都需要提供一个参数,以便 Printf() 能够按照你指定的方式将其值插入字符串中。

请用以下代码再次尝试你的程序:

fmt.Printf("remainder for %d is %d\n",i,i%4)

这段代码存在几个关键问题,主要涉及并发安全和切片操作的正确性。以下是详细分析和修复方案:

问题1:并发写入map导致的竞态条件

var m = make(map[int]int)

func writeToMap(key, value int) {
    m[key] = value // 并发写入会导致panic
}

修复方案:使用sync.Mutex或sync.Map

// 方案1:使用sync.Mutex
var (
    m   = make(map[int]int)
    mutex sync.Mutex
)

func safeWriteToMap(key, value int) {
    mutex.Lock()
    defer mutex.Unlock()
    m[key] = value
}

// 方案2:使用sync.Map(推荐用于高并发读场景)
var syncMap sync.Map

func safeWriteToSyncMap(key, value int) {
    syncMap.Store(key, value)
}

问题2:切片append操作的并发安全问题

var slice []int

func appendToSlice(value int) {
    slice = append(slice, value) // 并发append会导致数据丢失或panic
}

修复方案:使用互斥锁保护切片操作

var (
    slice []int
    sliceMutex sync.Mutex
)

func safeAppendToSlice(value int) {
    sliceMutex.Lock()
    defer sliceMutex.Unlock()
    slice = append(slice, value)
}

问题3:未使用WaitGroup等待goroutine完成

for i := 0; i < 10; i++ {
    go func() {
        // 工作代码
    }()
}
// 主goroutine可能提前退出

修复方案:正确使用sync.WaitGroup

var wg sync.WaitGroup

for i := 0; i < 10; i++ {
    wg.Add(1)
    go func(i int) {
        defer wg.Done()
        // 工作代码
    }(i)
}
wg.Wait() // 等待所有goroutine完成

完整修复示例:

package main

import (
    "sync"
)

type SafeContainer struct {
    m     map[int]int
    slice []int
    mutex sync.RWMutex
}

func (sc *SafeContainer) SafeWriteToMap(key, value int) {
    sc.mutex.Lock()
    defer sc.mutex.Unlock()
    sc.m[key] = value
}

func (sc *SafeContainer) SafeAppendToSlice(value int) {
    sc.mutex.Lock()
    defer sc.mutex.Unlock()
    sc.slice = append(sc.slice, value)
}

func main() {
    container := &SafeContainer{
        m:     make(map[int]int),
        slice: make([]int, 0),
    }
    
    var wg sync.WaitGroup
    
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func(i int) {
            defer wg.Done()
            container.SafeWriteToMap(i, i*2)
            container.SafeAppendToSlice(i)
        }(i)
    }
    
    wg.Wait()
}

这些修复确保了代码的线程安全性,避免了数据竞争和并发访问导致的问题。

回到顶部