Kita semua tahu bahawa keselamatan thread adalah sangat penting dalam pengaturcaraan serentak. Seterusnya, kami akan menganggap senario untuk menghasilkan semula urutan situasi yang tidak selamat, dan kemudian bercakap tentang cara menyelesaikan senario
Kita kini perlu menangani 1 ~ 100 untuk mencari faktorial mereka dan meletakkan hasilnya ke dalam peta
1! = 1 = 1 2! = 1 * 2 = 2 3! = 1 * 2 * 3 = 6 4! = 1 * 2 * 3 * 4 = 24 5! = 1 * 2 * 3 * 4 * 5 = 120 ... { 1: 1 2: 2 3: 6 4: 24 5: 120 ... }
var factorialMap = make(map[int]int) func Factorial(n int) { result := 1 for i := 1; i <= n; i++ { result *= i } factorialMap[n] = result } func main() { for i := 1; i < 10; i++ { Factorial(i) } for k, v := range factorialMap { fmt.Printf("%d 的阶乘是%d\n", k, v) } }
Hasil pelaksanaan kod di atas sebenarnya tidak Persoalannya, kenapa ada gangguan? Kerana ini peta dalam bahasa Go, ia sebenarnya tidak teratur mengikut pemahaman kami, yang pertama disimpan, pertama keluar, tetapi maaf, peta Golang tidak seperti ini. Tiada masalah dengan pelaksanaan di atas Pelajar yang berhati-hati mungkin mendapati bahawa versi kod ini tidak menggunakan konkurensi, bukan? Baiklah, mari kita teruskan perbaiki
var factorialMap = make(map[int]int) func Factorial(n int) { result := 1 for i := 1; i <= n; i++ { result *= i } factorialMap[n] = result } func main() { for i := 1; i < 10; i++ { go Factorial(i) } for k, v := range factorialMap { fmt.Printf("%d 的阶乘是%d\n", k, v) } }
Kita dapati bahawa versi serentak menambah a di hadapan panggilan untuk mengira faktorial fungsi go
Itu sahaja. Jangan memandang rendah ini go
, ini terlalu mengada-ada Sudah tentu, semua orang tahu bahawa ini adalah kata kunci untuk memulakan coroutine dalam bahasa Go.
Hasil pelaksanaan ialah tiada apa-apa keluaran kepada konsol Ini kerana hubungan pelaksanaan antara coroutine utama dan sub-coroutine Mari kita lukis gambar untuk memahami
Daripada gambar di atas, kita boleh didapati bahawa masa pelaksanaan coroutine utama adalah pendek (ditunjukkan sebagai relatif singkat), dan masa pelaksanaan sub-coroutine adalah agak panjang (ditunjukkan sebagai agak panjang) Kita mesti ingat bahawa sub-coroutine adalah relatif kepada coroutine utama semasa Jika coroutine utama tidak wujud lagi, tidak akan ada sub-coroutine
Jadi kod di atas tidak menghasilkan apa-apa dilaksanakan, tetapi sub-coroutine belum selesai Jika sub-coroutine belum selesai, bolehkah ada apa-apa dalam factorialMap
?
Ini membawa kepada soalan pertama kami, bagaimanakah coroutine utama menunggu sub-coroutine selesai melaksanakan sebelum keluar dari program. Kami kini menggunakan cara yang paling mudah dan paling mudah untuk memikirkan
var factorialMap = make(map[int]int) func Factorial(n int) { result := 1 for i := 1; i <= n; i++ { result *= i } factorialMap[n] = result } func main() { for i := 1; i < 100; i++ { go Factorial(i) } time.Sleep(time.Second * 3) for k, v := range factorialMap { fmt.Printf("%d 的阶乘是%d\n", k, v) } }
Apabila bilangan penyelarasan agak kecil, masalah ini mungkin tidak berlaku Sebaik sahaja bilangan penyelarasan menjadi besar, masalah akan berlaku muncul serta-merta.
Hasil pelaksanaan dalam gambar ialah Kesilapan penulisan peta serentakMengapa masalah ini berlaku? Tetapi jika 100 orang mengambil buah dari bakul, akan ada masalah Pertama, mungkin tidak cukup buah dalam bakul, kedua, semua orang mahu mengambilnya, yang pasti akan menimbulkan persaingan.
Memandangkan masalah di atas, kami memperkenalkan konsep kunci global. Ini agak-agak bila kita pergi tandas 100 orang nak guna tandas, tapi siapa yang ambik dulu dia yang pergi dulu, dan orang ni pun kunci tandas untuk menghalang orang lain masuk
var factorialMap = make(map[int]int) var lock sync.Mutex func Factorial(n int) { result := 1 for i := 1; i <= n; i++ { result *= i } // defer 不好理解 // defer func(){ // lock.Unlock() // 执行完解锁 // }() lock.Lock() // 执行时上锁 factorialMap[n] = result lock.Unlock() // 执行后解锁 } func main() { for i := 1; i < 100; i++ { go Factorial(i) } time.Sleep(time.Second * 3) for k, v := range factorialMap { fmt.Printf("%d 的阶乘是%d\n", k, v) } }
Hasil pelaksanaan 0 mungkin disebabkan oleh jenis data yang tidak dapat disimpan Anda tidak perlu risau tentang perkara ini
Dengan cara ini, kami telah menyelesaikan masalah persaingan sumber. Tetapi sebenarnya ada masalah lain, iaitu, kita masih perlu menunggu secara manual di coroutine utama, yang sangat buruk Bagaimana jika sub-coroutine tidak dapat menyelesaikannya dalam masa 3 saat?
Masalah ini ialah kita tidak mahu menunggu sub-coroutine secara manual dalam coroutine utama dengan kata lain, kita tidak 'Tidak mahu terus menulisnya dalam kod Berapa lama menunggu
Di sini kami telah memperkenalkan prinsip dalamanWaitGroup
var factorialMap = make(map[int]int) var lock sync.Mutex var wg sync.WaitGroup func Factorial(n int) { result := 1 for i := 1; i <= n; i++ { result *= i } lock.Lock() // 执行时上锁 factorialMap[n] = result lock.Unlock() // 执行后解锁 wg.Done() } func main() { for i := 1; i < 100; i++ { wg.Add(1) go Factorial(i) } wg.Wait() for k, v := range factorialMap { fmt.Printf("%d 的阶乘是%d\n", k, v) } }
WaitGroup. Anda boleh mempelajarinya sendiri, saya tidak akan masuk ke dalamnya sekarang.
Ringkasnya, WaitGroup
ialah bakul Setiap kali coroutine dibuka, pengecam ditambahkan pada bakul (Tambah fungsi setiap kali coroutine dilaksanakan, pengecam akan ditolak daripada bakul (Fungsi Selesai). Semak bakul. Jika kosong, ini bermakna coroutine telah dilaksanakan (fungsi Tunggu)
[Pembelajaran yang disyorkan: pergi tutorial video]
Atas ialah kandungan terperinci Artikel untuk membincangkan masalah persaingan sumber dalam bahasa Go. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!