成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

五分鐘搞懂 Golang noCopy 策略

開發
本文介紹了 Golang 中的 noCopy 策略,解釋了如何防止包含鎖的結構體被錯誤拷貝,以及如何使用 go vet 工具檢測潛在的拷貝問題。

本文介紹了 Golang 中的 noCopy 策略,解釋了如何防止包含鎖的結構體被錯誤拷貝,以及如何使用 go vet 工具檢測潛在的拷貝問題。

1. Sync.noCopy

在學習 Go 的 WaitGroup 代碼時,我注意到了 noCopy,并看到一個熟悉的注釋:"首次使用后不得復制"。

// A WaitGroup must not be copied after first use.
// 
// In the terminology of the Go memory model, a call to Done
//  “synchronizes before” the return of any Wait call that it unblocks.
type WaitGroup struct {
    noCopy noCopy

    state atomic.Uint64 // high 32 bits are counter, low 32 bits are waiter count.
    sema  uint32
}

搜索后發現,"首次使用后不得復制" 經常和 noCopy 一起出現。

// Note that it must not be embedded, due to the Lock and Unlock methods.
type noCopy struct{}

// Lock is a no-op used by -copylocks checker from `go vet`.
func (*noCopy) Lock()    {}
func (*noCopy) Unlock()  {}

通過查看 Go 1.23 中 `noCopy` 的定義發現:

  • noCopy 類型是一個空結構體。
  • noCopy 類型實現了兩種方法:Lock 和 Unlock,這兩種方法都是非操作方法。
  • 注釋強調,Lock 和 Unlock 由 go vet 檢查器使用。

noCopy 類型沒有實際的功能特性,只有通過思索和實驗才能理解其具體用途,以及為什么 "首次使用后不得復制"?

2. Go Vet 和 "鎖值錯誤傳遞"

當我們輸入以下命令:

go tool vet help copylocks

輸出:

copylocks: check for locks erroneously passed by value

Inadvertently copying a value containing a lock, such as sync.Mutex or
sync.WaitGroup, may cause both copies to malfunction. Generally such
values should be referred to through a pointer.

Go Vet 告訴我們在使用包含鎖(如 sync.Mutex 或 sync.WaitGroup)的值并通過值傳遞時,可能會導致意想不到的問題。例如:

package main

import (
 "fmt"
 "sync"
)

type T struct {
 lock sync.Mutex
}

func (t T) Lock() {
 t.lock.Lock()
}

func (t T) Unlock() {
 t.lock.Unlock()
}

func main() {
 var t T
 t.Lock()
 fmt.Println("test")
 t.Unlock()
 fmt.Println("finished")
}

運行這段代碼,將輸出錯誤信息:

// output
test
fatal error: sync: unlock of unlocked mutex

goroutine 1 [running]:
sync.fatal({0x4b2c9b?, 0x4a14a0?})
         /usr/local/go-faketime/src/runtime/panic.go:1031 +0x18
// ? go vet .
# noCopy
./main.go:12:9: Lock passes lock by value: noCopy.T contains sync.Mutex
./main.go:15:9: Unlock passes lock by value: noCopy.T contains sync.Mutex
Copy

錯誤原因是 Lock 和 Unlock 方法使用了值接收器 t,在調用方法時會創建 T 的副本,這意味著 Unlock 中的鎖實例與 Lock 中的鎖實例不匹配。

為了解決這個問題,可以將接收器改為指針類型:

func (t *T) Lock() {
 t.lock.Lock()
}

func (t *T) Unlock() {
 t.lock.Unlock()
}

同樣,在使用 Cond、WaitGroup 和其他包含鎖的類型時,需要確保它們在首次使用后不會被復制。例如:

package main

import (
 "fmt"
 "sync"
 "time"
)

func worker(id int, wg sync.WaitGroup) {
 defer wg.Done()
 fmt.Printf("Worker %d starting\n", id)
 time.Sleep(time.Second)
 fmt.Printf("Worker %d done\n", id)
}

func main() {
 var wg sync.WaitGroup

 for i := 1; i <= 3; i++ {
  wg.Add(1)
  go worker(i, wg)
 }

 wg.Wait()

 fmt.Println("All workers done!")
}

運行這段代碼,也會輸出錯誤信息:

/////
Worker 3 starting
Worker 1 starting
Worker 2 starting
Worker 1 done
Worker 3 done
Worker 2 done
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [semacquire]:
sync.runtime_Semacquire(0xc000108040?)

// ? go vet .
# noCopy
./main.go:9:24: worker passes lock by value: sync.WaitGroup contains sync.noCopy
./main.go:21:16: call of worker copies lock value: sync.WaitGroup contains sync.noCopy

要解決這個問題,可以使用相同的 wg 實例,大家可以自己試一下。有關 copylocks的更多信息可以查看 golang 官網。

3. 嘗試 go vet 檢測

go vet 的 noCopy 機制是一種防止結構體被拷貝的方法,尤其是那些包含同步原語(如 sync.Mutex 和 sync.WaitGroup)的結構,目的是防止意外的鎖拷貝,但這種防止并不是強制性的,是否拷貝需要由開發者檢測。例如:

package main

import "fmt"

type noCopy struct{}

func (*noCopy) Lock()   {}
func (*noCopy) Unlock() {}

type noCopyData struct {
 Val int32
 noCopy
}

func main() {
 c1 := noCopyData{Val: 10}
 c2 := c1
 c2.Val = 20
 fmt.Println(c1, c2)
}

上面的示例沒有任何實際用途,程序可以正常運行,但 go vet 會提示 "passes lock by value" 警告。這是一個嘗試 go vet 檢測機制的小練習。

不過,如果需要編寫與同步原語(如 sync.Mutex 和 sync.WaitGroup)相關的代碼,noCopy 機制可能就會有用。

4. 其他 noCopy 策略

據我們了解,go vet 可以檢測到未被嚴格禁止的潛在拷貝問題。有沒有嚴格禁止拷貝的策略?是的,有。讓我們看看 strings.Builder 的源代碼:

// A Builder is used to efficiently build a string using [Builder.Write] methods.
// It minimizes memory copying. The zero value is ready to use.
// Do not copy a non-zero Builder.
type Builder struct {
    addr *Builder // of receiver, to detect copies by value

    // External users should never get direct access to this buffer,
    // since the slice at some point will be converted to a string using unsafe,
    // also data between len(buf) and cap(buf) might be uninitialized.
    buf []byte
}

func (b *Builder) copyCheck() {
 if b.addr == nil {
  // This hack works around a failing of Go's escape analysis
  // that was causing b to escape and be heap allocated.
  // See issue 23382.
  // TODO: once issue 7921 is fixed, this should be reverted to
  // just "b.addr = b".
  b.addr = (*Builder)(abi.NoEscape(unsafe.Pointer(b)))
 } else if b.addr != b {
  panic("strings: illegal use of non-zero Builder copied by value")
 }
}


// Write appends the contents of p to b's buffer.
// Write always returns len(p), nil.
func (b *Builder) Write(p []byte) (int, error) {
    b.copyCheck()
    b.buf = append(b.buf, p...)
    return len(p), nil
}

關鍵點是:

b.addr = (*Builder)(abi.NoEscape(unsafe.Pointer(b)))

這行代碼的作用如下:

  • unsafe.Pointer(b):將 b 轉換為 unsafe.Pointer,以便與 abi.NoEscape 一起使用。
  • abi.NoEscape(unsafe.Pointer(b)):告訴編譯器 b 不會轉義,即可以繼續在棧而不是堆上分配。
  • (*Builder)(...): 將 abi.NoEscape 返回值轉換回 *Builder 類型,以便正常使用。

最后,b.addr 被設置為 b 本身的地址,這樣可以防止 Builder 被復制(在下面的邏輯中檢查 b.addr != b)。

使用有拷貝行為的 strings.Builder 會導致 panic:

func main() {
    var a strings.Builder
    a.Write([]byte("a"))
    b := a
    b.Write([]byte("b"))
}
// output
panic: strings: illegal use of non-zero Builder copied by value
goroutine 1 [running]:
strings.(*Builder).copyCheck(...)

5. 總結

  • 同步原語(如 sync.Mutex 和 sync.WaitGroup)不應被拷貝,因為一旦被拷貝,其內部狀態就會重復,從而導致并發問題。
  • 雖然 Go 本身并沒有提供嚴格防止拷貝的機制,但 noCopy 結構提供了一種非嚴格的機制,用于 go vet 工具的識別和拷貝檢測。
  • Go 中的某些源代碼會在運行時執行 noCopy 檢查并返回 panic,例如 strings.Builder 和 sync.Cond。
責任編輯:趙寧寧 來源: DeepNoMind
相關推薦

2025-01-21 07:39:04

Linux堆內存Golang

2024-12-11 07:00:00

面向對象代碼

2025-03-13 06:22:59

2024-12-04 16:12:31

2019-08-09 10:33:36

開發技能代碼

2025-03-18 09:20:00

Go語言Golang

2023-12-06 08:48:36

Kubernetes組件

2023-09-18 15:49:40

Ingress云原生Kubernetes

2022-05-23 09:10:00

分布式工具算法

2024-01-29 00:20:00

GolangGo代碼

2024-04-29 07:57:46

分布式流控算法

2024-11-28 08:54:19

GolangGo變量

2021-06-18 07:34:12

Kafka中間件微服務

2017-03-30 19:28:26

HBase分布式數據

2018-09-27 13:56:14

內網外網通信

2023-10-06 20:21:28

Python鏈表

2009-11-16 10:53:30

Oracle Hint

2020-06-16 08:47:53

磁盤

2021-05-28 07:38:20

內存溢出場景

2025-04-16 08:20:00

LinuxELF文件
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 国产清纯白嫩初高生视频在线观看 | 欧美在线a | 亚洲精品日韩一区二区电影 | 99精品国产成人一区二区 | 亚洲一二三区在线观看 | 久久精品国产99国产精品 | 成人精品国产一区二区4080 | 国产伦精品一区二区三区精品视频 | 精品国产乱码久久久久久久久 | 亚洲成人一区二区 | 一区免费| 日韩国产一区二区三区 | 美女艹b | 午夜精品一区二区三区在线观看 | 毛片久久久 | 亚洲欧美日韩中文字幕一区二区三区 | 亚洲福利一区 | 一区二区成人 | 日日夜夜免费精品视频 | 欧美日韩视频 | 成人免费精品视频 | 婷婷亚洲综合 | 成年人网站免费 | 91久久精品一区二区二区 | 日干夜操| 精品久久亚洲 | 日本在线视频中文字幕 | 精品久久久久久久久亚洲 | 91网站在线播放 | 久久久久久久久国产精品 | 九九九视频在线 | 亚洲午夜av久久乱码 | 美美女高清毛片视频免费观看 | 久久久久亚洲精品国产 | 国产成人自拍av | 日韩成人精品一区 | 国产精品九九九 | 国产乱码一二三区精品 | 精品视频久久久久久 | 亚洲成人自拍 | 日本精品视频一区二区 |