Rumah > pembangunan bahagian belakang > Golang > Menguasai Go Concurrency: Corak Penting untuk Aplikasi Berprestasi Tinggi

Menguasai Go Concurrency: Corak Penting untuk Aplikasi Berprestasi Tinggi

DDD
Lepaskan: 2024-12-18 22:26:12
asal
188 orang telah melayarinya

Mastering Go Concurrency: Essential Patterns for High-Performance Applications

Dalam membina aplikasi yang cekap dan berskala dalam Go, menguasai corak konkurensi adalah penting. Go, dengan goroutnya yang ringan dan saluran berkuasa, menyediakan persekitaran yang ideal untuk pengaturcaraan serentak. Di sini, kita akan menyelidiki beberapa corak konkurensi yang paling berkesan, termasuk kumpulan goroutine, baris gilir pekerja dan corak kipas keluar/masuk, bersama dengan amalan terbaik dan perangkap biasa yang perlu dielakkan.

Kolam Goroutine

Salah satu cara paling berkesan untuk mengurus konkurensi dalam Go ialah melalui penggunaan kolam goroutine. Kolam goroutine mengawal bilangan goroutine yang sedang dilaksanakan secara aktif pada bila-bila masa, yang membantu dalam menjimatkan sumber sistem seperti memori dan masa CPU. Pendekatan ini amat berguna apabila anda perlu mengendalikan sejumlah besar tugas secara serentak tanpa membebankan sistem.

Untuk melaksanakan kolam goroutine, anda mulakan dengan mencipta bilangan goroutin tetap yang membentuk kolam. Gorutin ini kemudiannya digunakan semula untuk melaksanakan tugas, mengurangkan overhed yang dikaitkan dengan mencipta dan memusnahkan gorouti secara berterusan. Berikut ialah contoh mudah tentang cara anda boleh melaksanakan kolam goroutine:

package main

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

type Job func()

func worker(id int, jobs <-chan Job, wg *sync.WaitGroup) {
    defer wg.Done()
    for job := range jobs {
        fmt.Printf("Worker %d starting job\n", id)
        job()
        fmt.Printf("Worker %d finished job\n", id)
    }
}

func main() {
    jobs := make(chan Job, 100)
    var wg sync.WaitGroup

    // Start 5 workers.
    for i := 1; i <= 5; i++ {
        wg.Add(1)
        go worker(i, jobs, &wg)
    }

    // Enqueue 20 jobs.
    for j := 1; j <= 20; j++ {
        job := func() {
            time.Sleep(2 * time.Second) // Simulate time-consuming task
            fmt.Println("Job completed")
        }
        jobs <- job
    }

    close(jobs) // Close the channel to indicate that no more jobs will be added.
    wg.Wait()  // Wait for all workers to finish.
    fmt.Println("All jobs have been processed")
}
Salin selepas log masuk
Salin selepas log masuk

Saiz Kolam yang Betul

Menentukan saiz optimum kolam goroutine anda adalah penting. Terlalu sedikit goroutine mungkin kurang menggunakan CPU, manakala terlalu banyak boleh menyebabkan perbalahan dan overhed yang tinggi. Anda perlu mengimbangi saiz kolam berdasarkan beban kerja dan kapasiti sistem. Memantau prestasi menggunakan alatan seperti pprof boleh membantu anda melaraskan saiz kolam mengikut keperluan.

Reka Bentuk dan Pengurusan Barisan Pekerja

Baris gilir pekerja pada asasnya ialah saluran yang menguruskan pengagihan tugas di kalangan goroutin dalam kumpulan. Pengurusan baris gilir yang berkesan memastikan tugas diagihkan secara sama rata, mengelakkan beberapa gorout daripada dibebankan manakala yang lain kekal melahu.

Begini cara anda boleh mereka bentuk baris gilir pekerja:

package main

import (
    "fmt"
    "sync"
)

type Worker struct {
    id       int
    jobQueue chan Job
    wg       *sync.WaitGroup
}

func NewWorker(id int, jobQueue chan Job, wg *sync.WaitGroup) *Worker {
    return &Worker{id: id, jobQueue: jobQueue, wg: wg}
}

func (w *Worker) Start() {
    defer w.wg.Done()
    for job := range w.jobQueue {
        fmt.Printf("Worker %d starting job\n", w.id)
        job()
        fmt.Printf("Worker %d finished job\n", w.id)
    }
}

func main() {
    jobQueue := make(chan Job, 100)
    var wg sync.WaitGroup

    // Start 5 workers.
    for i := 1; i <= 5; i++ {
        wg.Add(1)
        worker := NewWorker(i, jobQueue, &wg)
        go worker.Start()
    }

    // Enqueue 20 jobs.
    for j := 1; j <= 20; j++ {
        job := func() {
            fmt.Println("Job completed")
        }
        jobQueue <- job
    }

    close(jobQueue) // Close the channel to indicate that no more jobs will be added.
    wg.Wait()       // Wait for all workers to finish.
    fmt.Println("All jobs have been processed")
}
Salin selepas log masuk
Salin selepas log masuk

Corak Kipas-Keluar/Masuk

Corak kipas keluar/masuk kipas ialah teknik yang berkuasa untuk menyelaraskan dan menyelaraskan tugasan serentak. Corak ini terdiri daripada dua peringkat utama: kipas keluar dan kipas masuk.

Kipas-Keluar

Dalam peringkat fan-out, satu tugasan dibahagikan kepada beberapa subtugas yang lebih kecil yang boleh dilaksanakan secara serentak. Setiap subtugas diberikan kepada goroutine yang berasingan, membenarkan pemprosesan selari.

Fan-In

Dalam peringkat fan-in, hasil atau output daripada semua subtugas yang dilaksanakan secara serentak dikumpulkan dan digabungkan menjadi satu hasil. Peringkat ini menunggu semua subtugas selesai dan mengagregatkan keputusannya.

Berikut ialah contoh cara anda boleh melaksanakan corak kipas keluar/masuk untuk menggandakan nombor serentak:

package main

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

type Job func()

func worker(id int, jobs <-chan Job, wg *sync.WaitGroup) {
    defer wg.Done()
    for job := range jobs {
        fmt.Printf("Worker %d starting job\n", id)
        job()
        fmt.Printf("Worker %d finished job\n", id)
    }
}

func main() {
    jobs := make(chan Job, 100)
    var wg sync.WaitGroup

    // Start 5 workers.
    for i := 1; i <= 5; i++ {
        wg.Add(1)
        go worker(i, jobs, &wg)
    }

    // Enqueue 20 jobs.
    for j := 1; j <= 20; j++ {
        job := func() {
            time.Sleep(2 * time.Second) // Simulate time-consuming task
            fmt.Println("Job completed")
        }
        jobs <- job
    }

    close(jobs) // Close the channel to indicate that no more jobs will be added.
    wg.Wait()  // Wait for all workers to finish.
    fmt.Println("All jobs have been processed")
}
Salin selepas log masuk
Salin selepas log masuk

Primitif Penyegerakan

Primitif penyegerakan seperti WaitGroup, Mutex dan saluran adalah penting untuk menyelaraskan goroutin dan memastikan program serentak anda berfungsi dengan betul.

WaitGroup

WaitGroup digunakan untuk menunggu koleksi goroutin selesai. Begini cara anda boleh menggunakannya:

package main

import (
    "fmt"
    "sync"
)

type Worker struct {
    id       int
    jobQueue chan Job
    wg       *sync.WaitGroup
}

func NewWorker(id int, jobQueue chan Job, wg *sync.WaitGroup) *Worker {
    return &Worker{id: id, jobQueue: jobQueue, wg: wg}
}

func (w *Worker) Start() {
    defer w.wg.Done()
    for job := range w.jobQueue {
        fmt.Printf("Worker %d starting job\n", w.id)
        job()
        fmt.Printf("Worker %d finished job\n", w.id)
    }
}

func main() {
    jobQueue := make(chan Job, 100)
    var wg sync.WaitGroup

    // Start 5 workers.
    for i := 1; i <= 5; i++ {
        wg.Add(1)
        worker := NewWorker(i, jobQueue, &wg)
        go worker.Start()
    }

    // Enqueue 20 jobs.
    for j := 1; j <= 20; j++ {
        job := func() {
            fmt.Println("Job completed")
        }
        jobQueue <- job
    }

    close(jobQueue) // Close the channel to indicate that no more jobs will be added.
    wg.Wait()       // Wait for all workers to finish.
    fmt.Println("All jobs have been processed")
}
Salin selepas log masuk
Salin selepas log masuk

Mutex

Mutex digunakan untuk melindungi sumber kongsi daripada akses serentak. Berikut ialah contoh:

package main

import (
    "fmt"
    "sync"
)

func doubleNumber(num int) int {
    return num * 2
}

func main() {
    numbers := []int{1, 2, 3, 4, 5}
    jobs := make(chan int)
    results := make(chan int)

    var wg sync.WaitGroup

    // Start 5 worker goroutines.
    for i := 0; i < 5; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            for num := range jobs {
                result := doubleNumber(num)
                results <- result
            }
        }()
    }

    // Send jobs to the jobs channel.
    go func() {
        for _, num := range numbers {
            jobs <- num
        }
        close(jobs)
    }()

    // Collect results from the results channel.
    go func() {
        wg.Wait()
        close(results)
    }()

    // Print the results.
    for result := range results {
        fmt.Println(result)
    }
}
Salin selepas log masuk

Mengendalikan Penutupan Anggun

Penutupan anggun adalah penting dalam sistem serentak untuk memastikan semua tugasan yang sedang dijalankan selesai sebelum program tamat. Begini cara anda boleh mengendalikan penutupan yang anggun menggunakan isyarat berhenti:

package main

import (
    "fmt"
    "sync"
)

func main() {
    var wg sync.WaitGroup
    for i := 0; i < 5; i++ {
        wg.Add(1)
        go func(id int) {
            defer wg.Done()
            fmt.Printf("Worker %d is working\n", id)
            // Simulate work
            time.Sleep(2 * time.Second)
            fmt.Printf("Worker %d finished\n", id)
        }(i)
    }
    wg.Wait()
    fmt.Println("All workers have finished")
}
Salin selepas log masuk

Penandaarasan dan Mengoptimumkan Kod Serentak

Penanda aras adalah penting untuk memahami prestasi kod serentak anda. Go menyediakan pakej ujian terbina dalam yang termasuk alatan untuk penanda aras.

Berikut ialah contoh cara anda boleh menanda aras fungsi serentak yang mudah:

package main

import (
    "fmt"
    "sync"
)

type Counter struct {
    mu    sync.Mutex
    count int
}

func (c *Counter) Increment() {
    c.mu.Lock()
    c.count++
    c.mu.Unlock()
}

func (c *Counter) GetCount() int {
    c.mu.Lock()
    defer c.mu.Unlock()
    return c.count
}

func main() {
    counter := &Counter{}
    var wg sync.WaitGroup

    for i := 0; i < 100; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            counter.Increment()
        }()
    }

    wg.Wait()
    fmt.Println("Final count:", counter.GetCount())
}
Salin selepas log masuk

Untuk menjalankan penanda aras, anda boleh menggunakan arahan go test dengan bendera -bench:

package main

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

func worker(id int, quit <-chan bool, wg *sync.WaitGroup) {
    defer wg.Done()
    for {
        select {
        case <-quit:
            fmt.Printf("Worker %d received quit signal\n", id)
            return
        default:
            fmt.Printf("Worker %d is working\n", id)
            time.Sleep(2 * time.Second)
        }
    }
}

func main() {
    quit := make(chan bool)
    var wg sync.WaitGroup

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

    time.Sleep(10 * time.Second)
    close(quit) // Send quit signal
    wg.Wait()   // Wait for all workers to finish
    fmt.Println("All workers have finished")
}
Salin selepas log masuk

Strategi Pengendalian Ralat

Ralat pengendalian dalam program serentak boleh mencabar kerana sifat goroutin tidak segerak. Berikut ialah beberapa strategi untuk mengendalikan ralat dengan berkesan:

Menggunakan Saluran

Anda boleh menggunakan saluran untuk menyebarkan ralat daripada goroutine ke goroutine utama.

package main

import (
    "testing"
    "time"
)

func concurrentWork() {
    var wg sync.WaitGroup
    for i := 0; i < 100; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            time.Sleep(2 * time.Second)
        }()
    }
    wg.Wait()
}

func BenchmarkConcurrentWork(b *testing.B) {
    for i := 0; i < b.N; i++ {
        concurrentWork()
    }
}
Salin selepas log masuk

Menggunakan Konteks

Pakej konteks menyediakan cara untuk membatalkan operasi dan menyebarkan ralat merentas gorouti.

go test -bench=. -benchmem -benchtime=10s
Salin selepas log masuk

Kesimpulannya, menguasai corak konkurensi dalam Go adalah penting untuk membina aplikasi yang mantap, berskala dan cekap. Dengan memahami dan melaksanakan kumpulan goroutine, baris gilir pekerja, corak kipas keluar/masuk dan menggunakan primitif penyegerakan yang sesuai, anda boleh meningkatkan prestasi dan kebolehpercayaan sistem serentak anda dengan ketara. Sentiasa ingat untuk mengendalikan ralat dengan anggun dan tanda aras kod anda untuk memastikan prestasi optimum. Dengan strategi ini, anda boleh memanfaatkan potensi penuh ciri serentak Go untuk membina aplikasi berprestasi tinggi.


Ciptaan Kami

Pastikan anda melihat ciptaan kami:

Pusat Pelabur | Hidup Pintar | Epos & Gema | Misteri Membingungkan | Hindutva | Pembangunan Elit | Sekolah JS


Kami berada di Medium

Tech Koala Insights | Dunia Epok & Gema | Medium Pusat Pelabur | Medium Misteri Membingungkan | Sains & Zaman Sederhana | Hindutva Moden

Atas ialah kandungan terperinci Menguasai Go Concurrency: Corak Penting untuk Aplikasi Berprestasi Tinggi. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

sumber:dev.to
Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan