GO協程(Goroutine)問題總結

在使用Go語言來編寫代碼時,遇到的一些問題總結一下
[參考文檔]:https://www.topgoer.com/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/goroutine.html

1. main()函數默認的Goroutine

場景再現:

今天在看到這個教程的時候,在自己的電腦上跑了一下示例的代碼。
發現了描述與我的執行結果不同的地方,官方文檔描述如下:

這一次的執行結果只打印了main goroutine done!,并沒有打印Hello Goroutine!。

但是我執行后的情況是,如圖:
 main()函數默認的Goroutine

可以看到,我最終的執行結果是都輸出了,而不是只輸出了

main goroutine done!
Why?

原因是——雖然 main() 函數中調用了 go hello(),主 goroutine 在打印完 main goroutine done! 后就會退出,但:

在主 goroutine 退出前,如果新啟動的 goroutine 有足夠的時間運行完,Hello Goroutine! 就會輸出。
上面這段代碼啟動了一個新 goroutine,但程序的執行是并發的,不是同步/阻塞的。

執行流程是:

go hello() 啟動了一個新 goroutine;fmt.Println("main goroutine done!") 被執行;如果此時 main() 返回前,新 goroutine 還沒來得及執行完,那它也會被強行終止;但如果它已經執行完了,就能看到打印的內容。

這兩句都成功輸出,是因為你的電腦配置比較好,執行速度非常快,新啟動的 goroutine 來得及在 main() 退出前完成打印。

正確做法:用 sync.WaitGroup 或 time.Sleep

2 . Go 協程(Goroutine)的兩個關鍵點

協程不能保證執行的順序,但是如果加了time.sleep的話,可以保障協程執行完畢

? Go 協程(Goroutine)的兩個關鍵點:

①. 協程是并發的,不能保證執行順序

go hello() 啟動后,什么時候運行是由 Go 調度器決定的。
主協程和子協程是“誰搶到 CPU 誰先跑”,誰先打印是不確定的。

所以:

go hello()
fmt.Println("main done")

有可能先打印 main done,也可能先打印 Hello,取決于當時調度情況。

② 加 time.Sleep() 可以“間接保障”子協程執行完

加 time.Sleep() 相當于強行讓主協程等一下,給子協程留時間執行完。

所以子協程通常會有時間執行完,看起來“像是被保障了執行”。
?但注意:time.Sleep() ≠ 可靠同步
雖然 time.Sleep() 很簡單,但它存在幾個問題:

問題點說明
? 不精準你不知道子協程到底需要多少時間,sleep 多了浪費,少了又執行不完
? 不可擴展如果你有多個協程,就很難 sleep 到合適的時間
? 適合臨時調試用于演示或實驗是可以的

? 正確做法:用 sync.WaitGroup


import ("fmt""sync"
)func hello(wg *sync.WaitGroup) {fmt.Println("Hello Goroutine!")wg.Done() // 協程結束,通知 WaitGroup
}func main() {var wg sync.WaitGroupwg.Add(1)        // 告訴 WaitGroup 等待 1 個協程go hello(&wg)    // 啟動協程wg.Wait()        // 等待所有協程結束fmt.Println("main goroutine done!")
}

這樣就能準確地等待協程執行完再退出,不用靠 sleep。

總結一句話:
time.Sleep() 是簡單粗暴的等待方式,可以在小程序中“湊合用”,但真正寫程序,用 sync.WaitGroup 等同步機制更穩、更準、更專業。

3.defer

3.1 defer使用對比
func hello(i int) {defer wg.Done() // goroutine結束就登記-1fmt.Println("Hello Goroutine!", i)
}func hello(i int){fmt.Println("Hello Goroutine!",i)wg.Done()
}
//在 正常情況下的效果是一樣的:都會確保在 goroutine 執行完成后調用 wg.Done(),
//從而通知 WaitGroup,減少一個等待計數。

? defer wg.Done() 的優勢:
defer 會在 函數返回前自動執行,即使函數中間發生了 panic(未被恢復),defer 也會運行(前提是沒有讓程序直接崩潰)。

這意味著:

func hello(i int) {defer wg.Done()// 如果這里出現錯誤,也能保證 Done 會執行fmt.Println("Hello Goroutine!", i)
}

更安全、穩健,防止遺漏。

? 手動調用 wg.Done() 有風險:

func hello(i int) {fmt.Println("Hello Goroutine!", i)wg.Done()
}

如果你寫了更復雜的邏輯,中途 return 或 panic 了,wg.Done() 可能根本執行不到,就會導致 wg.Wait() 永遠卡住。
? 總結:

寫法是否推薦原因
defer wg.Done()? 推薦更安全,即使中途出錯也能保證 Done 被調用
手動調用 wg.Done()?? 慎用必須確保函數末尾一定能執行到,否則容易漏調用

所以你的判斷是對的:“兩種寫法是一樣的”,功能上是對的;但為了避免未來的問題,推薦使用 defer 寫法,更健壯、易維護。

3.2defer的作用、在Goroutine中的使用案例以及執行順序
3.2.1 defer的作用

? defer 的作用
defer 用于延遲一個函數的執行,直到外圍函數(即當前函數)返回之前才調用。

換句話說:

無論當前函數中發生了什么(正常結束或提前 return),defer 注冊的語句都會在函數結束前自動執行。

📌 舉個例子說明:

func demo() {fmt.Println("start")defer fmt.Println("this is defer")fmt.Println("end")
}

輸出結果是:

start
end
this is defer

defer fmt.Println(“this is defer”) 被延遲執行到 demo() 函數退出前的最后一刻。

? defer 的常見用途

用途示例說明
釋放資源defer file.Close()防止文件忘記關閉
解鎖defer mu.Unlock()防止死鎖
記錄日志/退出操作defer log.Println("退出")確保函數末尾執行
Done 通知defer wg.Done()保證 goroutine 退出時減少計數
3.2.2 在Goroutine中的使用案例
package mainimport ("fmt""sync"
)var wg sync.WaitGroupfunc hello(i int) {defer wg.Done() // 必須在協程結束時調用 wg.Done() 通知 WaitGroup 協程結束fmt.Println("Hello Goroutine!", i)
}
func main() {for i := 0; i < 10; i++ {wg.Add(1)   // 告訴 WaitGroup 等待 1 個協程go hello(i) // 啟動另外一個goroutine去執行hello函數}wg.Wait() // 等待所有的 goroutine 結束fmt.Println("main goroutine done!")
}
3.2.3 多個defer的執行順序

defer 在 Go 中的執行順序就是棧結構的“先進后出(LIFO)”原則:

🧠 defer 的執行順序:后注冊,先執行
也就是:

func demo() {defer fmt.Println("1")defer fmt.Println("2")defer fmt.Println("3")
}

🧾 輸出結果:

3
2
1

📦 原理解釋:
當你寫多個 defer 時,Go 會把它們壓入一個棧中,函數結束時就從棧頂往下依次執行這些 defer。

行號執行內容進入 defer 棧
1defer fmt.Println("1")棧:[1]
2defer fmt.Println("2")棧:[1, 2]
3defer fmt.Println("3")棧:[1, 2, 3]
return前執行 defer從棧頂開始彈出輸出順序:3 → 2 → 1

? 常見用途
多個 defer 通常用于按順序清理資源,比如:

func handleFile() {f1, _ := os.Open("file1.txt")defer f1.Close() // 最后關閉f2, _ := os.Open("file2.txt")defer f2.Close() // 比 f1 先關閉fmt.Println("doing something...")
}

結果是 file2 會先關閉,然后 file1 再關閉 —— 和你打開文件的順序是相反的,這正是我們想要的行為(像“關門要倒著來”一樣)。

? 總結:

特性說明
順序多個 defer 是“后注冊,先執行”(LIFO)
應用場景清理資源(文件、鎖、連接等)、錯誤處理等
安全性即使函數 returnpanic,也會執行

4.主協程和其他協程的關系,主協退出了,其他的協程還執行嗎?

代碼使用了官網提供的:

package mainimport ("fmt""time"
)func main() {// 合起來寫go func() {i := 0for {i++fmt.Printf("new goroutine: i = %d\n", i)time.Sleep(time.Second)}}()i := 0for {i++fmt.Printf("main goroutine: i = %d\n", i)time.Sleep(time.Second)if i == 2 {break}}
}

執行結果:

main goroutine: i = 1
new goroutine: i = 1
main goroutine: i = 2
new goroutine: i = 2
new goroutine: i = 3Process finished with the exit code 0

證明了主協程結束,其他線程不會再執行

5. java/c/c++線程與go協程的對比(與OS線程)

特性Java / C 的線程(OS Thread)Go 的 goroutine
線程類型操作系統線程(內核線程)用戶級線程(協程)
線程模型1:1 模型M:N 模型
調度者操作系統Go 自帶的調度器(runtime)
映射關系每個語言線程對應一個 OS 線程多個 goroutine 映射到多個 OS 線程
棧內存初始大小通常 1MB~2MB(固定)起始約 2KB(可動態伸縮)
創建成本高(需要系統調用)極低(用戶態,幾乎無開銷)
調度成本高(內核態線程切換)低(用戶態線程切換)
并發數量限制一般幾千個十萬甚至百萬級
適合場景計算密集、高性能場景高并發、大量 I/O 場景
常用語言APIstd::thread, Threadgo myFunc()
內存使用效率相對較低非常高

🔍 示例類比:

類比Java / C 的線程Go 的 goroutine
比喻重型卡車:開銷大但能干活自行車大軍:輕量且靈活
調度員操作系統Go 自己的調度器
數量幾千個已很吃力十萬個都輕輕松松

? 圖示說明

Java / C         =>        1:1 線程模型
┌──────────┐         ┌──────────┐
│ Thread A │───────?│  OS 線程 A │
│ Thread B │───────?│  OS 線程 B │
└──────────┘         └──────────┘Go              =>        M:N 線程模型
┌──────────────┐
│ goroutine 1  │
│ goroutine 2  │
│ goroutine 3  │──┐
│ goroutine 4  │  │
│ goroutine 5  │  ├──? 被 Go runtime 調度
│ goroutine N  │──┘     分配到 OS 線程 A/B/C…
└──────────────┘

? 總結一句話:
Java 和 C 的線程就是系統線程(1:1),重量級。
Go 的 goroutine 是用戶級線程,輕量可擴展(M:N),適合高并發。

6.并發與并行

6.1并發 vs 并行 示意圖
+-----------------------+         +-------------------------+
|       單核 CPU        |         |        多核 CPU          |
| (1 個核心處理器核)     |         |   (多個核心同時工作)      |
+-----------------------+         +-------------------------+
|                       |         |                         |
|  ┌───────────────┐    |         |  ┌───────────────┐      |
|  │   核心 CPU 核  │    |         |  │  核心 CPU 核 1 │      |
|  │               │    |         |  ├───────────────┤      |
|  │ 任務1          │    |         |  │ 任務1 (并行執行)│      |
|  │任務2 (并發切換) │ ←──┐      |  │任務2 (并行執行)│      |
|  └───────────────┘    |  |      |  └───────────────┘      |
|  任務1和任務2“交替”執行 |  |      |  多個任務“真正同時”執行   |
|                       |  |      |                         |
+-----------------------+  |      +-------------------------+|并發(Concurrency) |      并行(Parallelism)任務切換看似同時進行 |      物理上真正同時執行
6.2Go 的 G-P-M 調度模型簡化示意
+----------------------------+
|           G (Goroutine)    |  // 輕量級任務
+----------------------------+│▼
+----------------------------+
|         P (Processor)       |  // 邏輯處理器(虛擬CPU核)
|  數量 = GOMAXPROCS 的值     |
+----------------------------+│▼
+----------------------------+
|       M (Machine/線程)      |  // 真實的 OS 線程
+----------------------------+│▼真正執行代碼的 CPU 核心

你創建的很多 G (goroutine),由有限個 P(由 GOMAXPROCS 控制) 調度到 M(操作系統線程) 上執行。

當 GOMAXPROCS=1,只有一個 P,也就是說同一時刻只有一個 M 在執行 goroutine,表現為“并發”,但不是“并行”。

當 GOMAXPROCS>1,多個 P 和 M 允許 goroutine 并行運行在多個 CPU 核心上。

6.3舉個例子

🍳 并發(Concurrency):
就像一個廚師(單核 CPU),輪流做多個菜(任務)。他可能:


先把鍋燒熱,然后去切菜,再回來翻炒,接著煮湯……

雖然這些菜好像都在“同時準備”,但其實是一個人來回切換工作狀態完成的。

特點:


看起來同時,其實是輪流進行的本質上是 任務調度,而不是并行執行

👨?🍳👨?🍳 并行(Parallelism):
多個廚師(多核 CPU)在多個灶臺上,真正同時做多個菜(任務):

一人炒菜,一人煮湯,一人蒸飯……

特點:

真正同時執行每個任務由不同核心獨立運行

💡 套用到 Go 的場景里:

go func() 就像是安排一個菜讓廚師做runtime.GOMAXPROCS(1) 就是告訴廚房:“我只配一個廚師”(不管你有幾個灶臺)Go 的調度器就像廚房總管,會安排炒什么菜、先做哪個、后做哪個

7. 緩沖信道和無緩沖信道

🌟 一張表格對比
特性無緩沖通道 (make(chan T))帶緩沖通道 (make(chan T, N))
是否有緩沖區? 沒有,容量為 0? 有,容量為 N(N ≥ 1)
是否阻塞發送方? 會,直到接收方準備好? 不會,只要緩沖區沒滿
是否阻塞接收方? 會,直到發送方發送? 不會,只要緩沖區有數據
典型應用同步通信,控制執行時機異步通信,解耦發送與接收
性能慢,但同步、控制精準快,但可能增加復雜性
死鎖風險高(若不成對出現)低(發送和接收可以脫鉤)
是否 FIFO? 是? 是
? 示例對比:無緩沖通道

package mainimport "fmt"func main() {ch := make(chan int) // 無緩沖通道go func() {val := <-chfmt.Println("接收成功:", val)}()ch <- 10              // 阻塞直到接收方準備好fmt.Println("發送成功")
}

執行順序:

接收成功: 10
發送成功

👉 注意:如果你把 發送(ch <- 10) 放在 go 啟動接收協程 之前,就會死鎖!

? 示例對比:帶緩沖通道
package mainimport "fmt"func main() {ch := make(chan int, 1) // 帶緩沖通道,容量為1ch <- 10                // ? 不阻塞fmt.Println("發送成功")go func() {val := <-chfmt.Println("接收成功:", val)}()
}

執行順序:

發送成功
接收成功: 10

👉 注意:即使接收方還沒準備好,發送也不會阻塞(只要緩沖區有空間)。

🧠 總結一句話:
無緩沖通道:必須“手遞手”,發送和接收必須同步發生。

帶緩沖通道:允許“放下再走”,先發送存儲,之后再接收。

🤔我的理解【ASK ChatGPT】
我的理解

無論是先發送還是先接收,都不應該主程序阻塞后續的程序,否則接收不到,或者發送不了。
無論先發送還是接收,都應該在下一步前建立一個協程來進行類似監聽的協程來阻塞協程這樣才能保證發送接收同步進行嗎?
無論是發送(val<-10),還是接收(<-val)都會阻塞當前的協程,我說的對嗎

GPT回答

? 你的原話分析:

?“無論是先發送還是先接收,都不應該主程序阻塞后續的程序,否則接收不到,或者發送不了。”
? 完全正確!

如果你在 main() 主線程中直接執行 val := <-ch 或 ch <- 10,主 goroutine 會阻塞,導致之后的 goroutine 根本不會被調度運行。

所以要讓 發送/接收那一方跑在 goroutine 中,才能讓另一方執行并完成通信。

?“無論先發送還是接收,都應該在下一步前建立一個協程來進行類似監聽的協程來阻塞協程這樣才能保證發送接收同步進行嗎?”
? 非常正確,表達也很清晰。

Go 的無緩沖通道通信是 兩個 goroutine 的同步配合行為。

不論誰先運行,都必須有 另一個 goroutine 在等待,通信才能發生。

? 舉個常用結構例子:

go func() {ch <- 10  // goroutine A 發送
}()go func() {val := <-ch  // goroutine B 接收
}()

兩個 goroutine 各自阻塞在 send/recv 點,一旦兩者都就緒,通信立刻完成。

?“無論是發送(val <- 10),還是接收(<-val)都會阻塞當前的協程,我說的對嗎?”
? 完全正確!

操作是否阻塞說明
ch <- 10? 會阻塞如果沒有協程在接收,就阻塞
<-ch? 會阻塞如果沒有協程在發送,就阻塞

? 只要通道是無緩沖的,這兩個操作都必須等對方準備好才能繼續。

? 最核心的一句話總結:
無緩沖通道中的發送和接收,必須發生在兩個獨立的 goroutine 中,才能順利配對并完成同步。

8. channel關閉(close)

8.1 channel 關閉(close)≠ 銷毀

Go 里沒有顯式的“銷毀”channel操作,只有 關閉(close) 操作。

close(ch) 表示:

禁止再向通道寫入數據(寫入會 panic)可以繼續讀取通道中剩余的緩沖數據讀取到空了之后,再讀取會得到零值(對應類型的默認值)并返回 ok=false
8.2 關閉 channel 后為什么“只能讀取一次”?
通道關閉后,只能讀取到剩余的數據,讀取完就沒有了。你可以多次從關閉的 channel 讀取數據,直到讀完所有剩余數據。但是如果你用 for range ch 循環,循環會在讀完所有數據后自動結束。

重點:
for range ch 循環 只能執行一次,因為通道中的數據只會被消費一次,且通道關閉后不再接收新數據。

8.3 不關閉 channel,能讀多次嗎?
如果 不關閉 channel,就不能用 for range ch 這種方式循環讀取,因為它會阻塞等待新的數據,直到 channel 關閉。但你可以用手動讀取(val, ok := <-ch)不斷讀數據,只要有人往里面寫,就能持續接收。多個接收者從同一個 channel 讀取是“搶占式”的,數據會被分配給其中一個,不能保證所有接收者都能讀到相同數據。
8.4 總結
狀態是否能寫入是否能讀取多次備注
channel 已關閉不允許(寫會 panic)可以讀取剩余數據,讀完返回零值+ok=false只能消費剩余數據,不能寫入
channel 未關閉允許可以多次讀取(取決于寫入情況)讀取會阻塞等待數據
  1. 舉個例子說明
package mainimport "fmt"func main() {ch := make(chan int, 3)ch <- 1ch <- 2ch <- 3close(ch) // 關閉通道for i := 0; i < 5; i++ {val, ok := <-chfmt.Println(val, ok)}
}

輸出:

1 true
2 true
3 true
0 false  // 通道關閉且無剩余數據,返回類型零值,ok=false
0 false

如果你想多次“重新讀取”同一批數據,就需要把數據保存到 slice 或其他容器,而不是依賴 channel 本身,因為 channel 數據是一次性消費的。

9.匿名函數(內聯函數)

官方文檔的Goroutine池中提供了

package mainimport ("fmt""math/rand"
)type Job struct {// idId int// 需要計算的隨機數RandNum int
}type Result struct {// 這里必須傳對象實例job *Job// 求和sum int
}func main() {// 需要2個管道// 1.job管道jobChan := make(chan *Job, 128)// 2.結果管道resultChan := make(chan *Result, 128)// 3.創建工作池createPool(64, jobChan, resultChan)// 4.開個打印的協程go func(resultChan chan *Result) {// 遍歷結果管道打印for result := range resultChan {fmt.Printf("job id:%v randnum:%v result:%d\n", result.job.Id,result.job.RandNum, result.sum)}}(resultChan) //①匿名函數var id int// 循環創建job,輸入到管道for {id++// 生成隨機數r_num := rand.Int()job := &Job{Id:      id,RandNum: r_num,}jobChan <- job}
}// 創建工作池
// 參數1:開幾個協程
func createPool(num int, jobChan chan *Job, resultChan chan *Result) {// 根據開協程個數,去跑運行for i := 0; i < num; i++ {go func(jobChan chan *Job, resultChan chan *Result) {// 執行運算// 遍歷job管道所有數據,進行相加for job := range jobChan {// 隨機數接過來r_num := job.RandNum// 隨機數每一位相加// 定義返回值var sum intfor r_num != 0 {tmp := r_num % 10sum += tmpr_num /= 10}// 想要的結果是Resultr := &Result{job: job,sum: sum,}//運算結果扔到管道resultChan <- r}}(jobChan, resultChan)//②匿名函數}
}

對②匿名函數解釋

  • (jobChan, resultChan)createPool(num int, jobChan chan *Job, resultChan chan *Result)處對應的參數值,
  • 然后(jobChan, resultChan)又被當作參數傳入了go func(jobChan chan *Job, resultChan chan *Result)

用匿名函數的好處是 不需要額外寫一個函數名,邏輯上簡潔一些。

10.select處理多種類型事件例子

常見用法舉例
假設你有幾個不同類型的事件通過不同通道傳遞,你可以寫類似的代碼:

package mainimport ("fmt""time"
)func eventA(ch chan string) {time.Sleep(time.Second * 1)ch <- "Event A occurred"
}func eventB(ch chan int) {time.Sleep(time.Second * 2)ch <- 42
}func eventC(ch chan bool) {time.Sleep(time.Second * 3)ch <- true
}func main() {chA := make(chan string)chB := make(chan int)chC := make(chan bool)go eventA(chA)go eventB(chB)go eventC(chC)for i := 0; i < 3; i++ {select {case msg := <-chA:fmt.Println("Received from chA:", msg)case val := <-chB:fmt.Println("Received from chB:", val)case flag := <-chC:fmt.Println("Received from chC:", flag)}}
}

解釋

  • 你啟動3個 goroutine,它們各自延遲不同時間后往對應通道寫入不同類型的事件數據。

  • 主協程中用一個 select 監聽這3個通道。

  • 每當任意一個通道有數據,就進入對應 case 執行,打印接收到的事件。

  • 循環3次保證3個事件都能被處理。

重點總結

  • select 可以監聽任意多個通道。

  • 每個 case 可以處理不同類型的數據。

  • 只要某個通道準備好數據,select 就會立即觸發對應的 case。

  • select 本身不是“處理多種類型數據”的結構,但結合不同通道的類型可以做到這一點。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/86509.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/86509.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/86509.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

uniapp微信小程序視頻實時流+pc端預覽方案

方案類型技術實現是否免費優點缺點適用場景延遲范圍開發復雜度?WebSocket圖片幀?定時拍照Base64傳輸? 完全免費無需服務器 純前端實現高延遲高流量 幀率極低個人demo測試 超低頻監控500ms-2s???RTMP推流?TRTC/即構SDK推流? 付費方案 &#xff08;部分有免費額度&#x…

分布式鎖實戰:Redisson vs. Redis 原生指令的性能對比

分布式鎖實戰&#xff1a;Redisson vs. Redis 原生指令的性能對比 引言 在DIY主題模板系統中&#xff0c;用戶可自定義聊天室的背景、圖標、動畫等元素。當多個運營人員或用戶同時修改同一模板時&#xff0c;若沒有鎖機制&#xff0c;可能出現“甲修改了背景色&#xff0c;乙…

C++ 設計模式《復制粘貼的奇跡:小明的原型工廠》

&#x1f468;?&#x1f393; 模式名稱&#xff1a;原型模式&#xff08;Prototype Pattern&#xff09; &#x1f4d6; 背景故事 創業初期&#xff0c;小明每天加班寫配送路線、配送策略、營銷套餐。可當業務做大后&#xff0c;他發現大家常常下單“上次那個套餐”—— “老…

【Elasticsearch】映射:fielddata 詳解

映射&#xff1a;fielddata 詳解 1.fielddata 是什么2.fielddata 的工作原理3.主要用法3.1 啟用 fielddata&#xff08;通常在 text 字段上&#xff09;3.2 監控 fielddata 使用情況3.3 清除 fielddata 緩存 4.使用場景示例示例 1&#xff1a;對 text 字段進行聚合示例 2&#…

開源 vGPU 方案:HAMi,實現細粒度 GPU 切分

本文主要分享一個開源的 GPU 虛擬化方案&#xff1a;HAMi&#xff0c;包括如何安裝、配置以及使用。 相比于上一篇分享的 TimeSlicing 方案&#xff0c;HAMi 除了 GPU 共享之外還可以實現 GPU core、memory 得限制&#xff0c;保證共享同一 GPU 的各個 Pod 都能拿到足夠的資源。…

PlayDiffusion上線:AI語音編輯進入“無痕時代”

在語音合成與語音編輯領域&#xff0c;一個長期存在的挑戰是如何在修改語音內容的同時&#xff0c;保持原始語音的自然性、連貫性和說話人特征。近日&#xff0c;一款名為 PlayDiffusion 的新型 AI 語音修復模型應運而生&#xff0c;成功實現了這一目標。 PlayDiffusion 是一個…

2025年能源電力系統與流體力學國際會議 (EPSFD 2025)

2025年能源電力系統與流體力學國際會議&#xff08;EPSFD 2025&#xff09;將于本年度在美麗的杭州盛大召開。作為全球能源、電力系統以及流體力學領域的頂級盛會&#xff0c;EPSFD 2025旨在為來自世界各地的科學家、工程師和研究人員提供一個展示最新研究成果、分享實踐經驗及…

微信小程序前端面經

一、技術棧與編碼能力&#xff08;10min&#xff09; 1. Vue 3 & Composition API Q1&#xff1a;請解釋一下 ref 和 reactive 的區別&#xff1f;你在項目中是如何使用的&#xff1f; 答&#xff1a;ref是包裝一個原始值或對象&#xff0c;通過.value訪問&#xff0c;r…

rknn toolkit2搭建和推理

安裝Miniconda Miniconda - Anaconda Miniconda 選擇一個 新的 版本 &#xff0c;不用和RKNN的python版本保持一致 使用 ./xxx.sh進行安裝 下面配置一下載源 # 清華大學源&#xff08;最常用&#xff09; conda config --add channels https://mirrors.tuna.tsinghua.edu.cn…

WordPress插件:AI多語言寫作與智能配圖、免費AI模型、SEO文章生成

厭倦手動寫WordPress文章&#xff1f;AI自動生成&#xff0c;效率提升10倍&#xff01; 支持多語言、自動配圖、定時發布&#xff0c;讓內容創作更輕松&#xff01; AI內容生成 → 不想每天寫文章&#xff1f;AI一鍵生成高質量內容&#xff01;多語言支持 → 跨境電商必備&am…

Houdini POP入門學習07 - 分組

使用PopGroup可對粒子進行分組操作&#xff0c;并通過表達式從而更靈活的處理粒子行為。 1.創建box作為發射器&#xff0c;連接popnet節點。 2.雙擊進入popnet&#xff0c;添加popwind添加向上風力。現在播放粒子可見粒子向上方移動。 3.添加popgroup進行分組&#xff0c;開啟…

機器學習復習3--模型評估

誤差與過擬合 我們將學習器對樣本的實際預測結果與樣本的真實值之間的差異稱為&#xff1a;誤差&#xff08;error&#xff09;。 誤差定義&#xff1a; ①在訓練集上的誤差稱為訓練誤差&#xff08;training error&#xff09;或經驗誤差&#xff08;empirical error&#x…

Docker 鏡像上傳到 AWS ECR:從構建到推送的全流程

一、在 EC2 實例中安裝 Docker&#xff08;適用于 Amazon Linux 2&#xff09; 步驟 1&#xff1a;連接到 EC2 實例 ssh -i your-key.pem ec2-useryour-ec2-public-ip步驟 2&#xff1a;安裝 Docker sudo yum update -y sudo amazon-linux-extras enable docker sudo yum in…

MobileNet 改進:基于MobileNetV2和SSPP的圖像分類

1.創新點分析 在計算機視覺領域,高效的圖像分類模型一直是研究熱點。 本文將詳細解析一個結合了MobileNetV2和空間金字塔池化(SSPP)的深度學習模型實現。 模型概述 這個代碼實現了一個輕量級但功能強大的圖像分類器,主要包含兩個核心組件: MobileNetV2作為特征提取器 自定…

Java中List的forEach用法詳解

在 Java 中&#xff0c;List.forEach() 是 Java 8 引入的一種簡潔的遍歷集合元素的方法。它基于函數式編程思想&#xff0c;接受一個 Consumer 函數式接口作為參數&#xff0c;用于對集合中的每個元素執行操作。 基本語法 java 復制 下載 list.forEach(consumer); 使用示…

涂鴉T5AI手搓語音、emoji、otto機器人從入門到實戰

“&#x1f916;手搓TuyaAI語音指令 &#x1f60d;秒變表情包大師&#xff0c;讓萌系Otto機器人&#x1f525;玩出智能新花樣&#xff01;開整&#xff01;” &#x1f916; Otto機器人 → 直接點明主體 手搓TuyaAI語音 → 強調 自主編程/自定義 語音控制&#xff08;TuyaAI…

計算機視覺與深度學習 | 基于MATLAB的相機標定

基于MATLAB的相機標定:原理、步驟與代碼實現 相機標定 基于MATLAB的相機標定:原理、步驟與代碼實現MATLAB相機標定完整流程1. 準備工作2. 采集標定圖像3. 導入圖像并檢測角點4. 生成世界坐標5. 執行相機標定6. 分析標定結果7. 應用標定結果校正圖像相機標定關鍵概念相機參數類…

物聯網專業核心課程以及就業方向

物聯網專業作為信息技術與產業應用深度融合的交叉學科&#xff0c;其課程體系覆蓋硬件、軟件、網絡、數據等全鏈條技術&#xff0c;就業方向則隨智能技術普及呈現多元化趨勢。以下是基于最新行業動態與教育實踐的系統分析&#xff1a; &#x1f4da; 一、物聯網專業核心課程體系…

mac 安裝homebrew (nvm 及git)

mac 安裝nvm 及git 萬惡之源 mac 安裝這些東西離不開Xcode。及homebrew 一、先說安裝git步驟 通用&#xff1a; 方法一&#xff1a;使用 Homebrew 安裝 Git&#xff08;推薦&#xff09; 步驟如下&#xff1a;打開終端&#xff08;Terminal.app&#xff09; 1.安裝 Homebrew…

vue3 定時器-定義全局方法 vue+ts

1.創建ts文件 路徑&#xff1a;src/utils/timer.ts 完整代碼&#xff1a; import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 創建定時器con…