Golang ialah bahasa pengaturcaraan dengan kecekapan pelaksanaan yang tinggi, dan ciri pengaturcaraan serentaknya digunakan secara meluas dalam pelbagai senario permintaan. Dalam perpustakaan standard Golang, banyak primitif penyegerakan disediakan untuk melaksanakan kawalan konkurensi, seperti mutex, saluran, dsb. Pada masa yang sama, kami juga boleh menggunakan beberapa strategi pengoptimuman prestasi untuk meningkatkan lagi kecekapan menjalankan program. Artikel ini akan memperkenalkan cara menggabungkan primitif penyegerakan dan strategi pengoptimuman prestasi di Golang, dan menyediakan contoh kod khusus.
1. Pengenalan dan senario aplikasi primitif penyegerakan
Primitif penyegerakan direka untuk menyelaraskan urutan pelaksanaan dan akses data antara berbilang goroutin serentak. Di Golang, primitif penyegerakan yang paling biasa digunakan ialah mutex, cond dan waitgroup.
1.1 mutex
mutex ialah kunci mutex yang melindungi kod dalam bahagian kritikal untuk memastikan berbilang goroutine tidak mengakses sumber kongsi pada masa yang sama. Mutex menggunakan dua kaedah, Lock() dan Unlock(), yang pertama digunakan untuk memperoleh kunci, dan yang terakhir digunakan untuk melepaskan kunci.
Secara amnya, apabila berbilang goroutine perlu membaca dan menulis sumber kongsi yang sama, kami boleh menggunakan mutex untuk memastikan akses selamat kepada sumber tersebut. Berikut ialah kod sampel menggunakan mutex:
package main import ( "fmt" "sync" ) var ( count int mux sync.Mutex ) func increment() { mux.Lock() count++ mux.Unlock() } func main() { var wg sync.WaitGroup for i := 0; i < 1000; i++ { wg.Add(1) go func() { defer wg.Done() increment() }() } wg.Wait() fmt.Println("Count:", count) }
Dalam kod di atas, kami mencipta kiraan pembolehubah global dan berbilang goroutin menambah kiraan dengan memanggil fungsi kenaikan. Untuk memastikan akses selamat untuk mengira, kami menggunakan mutex untuk kawalan mutex.
1.2 cond
cond ialah pembolehubah keadaan yang boleh menghantar isyarat antara goroutine. Apabila goroutine menunggu syarat tertentu untuk dipenuhi, ia boleh menggantung dirinya melalui kaedah Tunggu cond, dan kemudian meneruskan pelaksanaan selepas syarat dipenuhi.
Senario di mana cond digunakan secara amnya adalah model pengeluar-pengguna Kod contoh khusus adalah seperti berikut:
package main import ( "fmt" "sync" ) var ( count int maxCount = 10 condition = sync.NewCond(&sync.Mutex{}) ) func produce() { condition.L.Lock() for count > maxCount { condition.Wait() } count++ fmt.Println("Produce:", count) condition.L.Unlock() condition.Signal() } func consume() { condition.L.Lock() for count <= 0 { condition.Wait() } count-- fmt.Println("Consume:", count) condition.L.Unlock() condition.Signal() } func main() { var wg sync.WaitGroup for i := 0; i < 10; i++ { wg.Add(2) go func() { defer wg.Done() produce() }() go func() { defer wg.Done() consume() }() } wg.Wait() }
Dalam kod di atas, kami melaksanakan model pengeluar-pengguna yang mudah melalui cond. Apabila kiraan melebihi maxCount, pengeluar menggantung dirinya dengan memanggil kaedah Tunggu cond, dan kemudian membangunkan goroutine menunggu lain dengan memanggil kaedah Isyarat cond selepas pengguna menggunakan.
1.3 waitgroup
waitgroup ialah kaunter yang boleh menunggu sekumpulan goroutine dilaksanakan sebelum meneruskan. waitgroup menyediakan tiga kaedah Add(), Done() dan Wait() Dua yang pertama digunakan untuk menambah dan mengurangkan pembilang, dan yang terakhir digunakan untuk menunggu pembilang kembali kepada sifar.
Senario penggunaan kumpulan tunggu biasanya apabila goroutin utama menunggu goroutin serentak lain selesai sebelum meneruskan ke langkah seterusnya. Berikut ialah kod sampel kumpulan tunggu:
package main import ( "fmt" "sync" ) var ( count int wg sync.WaitGroup ) func increment() { defer wg.Done() count++ } func main() { for i := 0; i < 1000; i++ { wg.Add(1) go increment() } wg.Wait() fmt.Println("Count:", count) }
Dalam kod di atas, kami menggunakan kumpulan tunggu untuk memastikan semua goroutin dilaksanakan sebelum mengeluarkan nilai kiraan.
2. Pengenalan kepada strategi pengoptimuman prestasi dan senario aplikasi
Di Golang, terdapat beberapa strategi pengoptimuman prestasi yang boleh membantu kami meningkatkan kecekapan menjalankan program. Berikut memperkenalkan beberapa strategi pengoptimuman yang biasa digunakan dan memberikan contoh kod khusus. . Oleh itu, menggunakan kumpulan goroutine untuk menggunakan semula goroutine yang telah dibuat ialah strategi pengoptimuman prestasi.
Berikut ialah contoh kod yang menggunakan kumpulan goroutine untuk memproses tugas secara serentak:
package main import ( "fmt" "runtime" "sync" ) type Task struct { ID int } var tasksCh chan Task func worker(wg *sync.WaitGroup) { defer wg.Done() for task := range tasksCh { fmt.Println("Processing task:", task.ID) } } func main() { numWorkers := runtime.NumCPU() runtime.GOMAXPROCS(numWorkers) tasksCh = make(chan Task, numWorkers) var wg sync.WaitGroup for i := 0; i < numWorkers; i++ { wg.Add(1) go worker(&wg) } for i := 0; i < 10; i++ { tasksCh <- Task{ID: i} } close(tasksCh) wg.Wait() }
Kunci Mutex akan menyebabkan masalah persaingan kunci dalam senario konkurensi tinggi, mengakibatkan kemerosotan prestasi. Untuk meningkatkan prestasi serentak program, kami boleh menggunakan struktur data tanpa kunci untuk mengelakkan pertikaian kunci.
Berikut ialah kod sampel yang menggunakan operasi atom dalam pakej penyegerakan/atom untuk melaksanakan pembilang bebas kunci:
package main import ( "fmt" "sync/atomic" ) var count int32 func increment() { atomic.AddInt32(&count, 1) } func main() { for i := 0; i < 1000; i++ { go increment() } fmt.Println("Count:", atomic.LoadInt32(&count)) }
Dalam pembangunan sebenar, kami sering menghadapi senario yang memerlukan kedua-dua memastikan keselamatan serentak dan meningkatkan kecekapan operasi program. Berikut ialah contoh kod yang menggabungkan mutex dan struktur data tanpa kunci:
package main import ( "fmt" "sync" "sync/atomic" ) var ( count int32 mux sync.Mutex ) func increment() { atomic.AddInt32(&count, 1) } func main() { var wg sync.WaitGroup for i := 0; i < 1000; i++ { wg.Add(1) go func() { defer wg.Done() mux.Lock() increment() mux.Unlock() }() } wg.Wait() fmt.Println("Count:", atomic.LoadInt32(&count)) }
Dalam kod di atas, kami menggunakan mutex untuk memastikan akses selamat untuk mengira dan menggunakan operasi atom dalam pakej atom untuk meningkatkan kiraan. Dengan menggabungkan mutex dan struktur data tanpa kunci, kami bukan sahaja memastikan keselamatan serentak, tetapi juga meningkatkan kecekapan menjalankan program.
Atas ialah kandungan terperinci Aplikasi gabungan primitif penyegerakan dan strategi pengoptimuman prestasi di Golang. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!