ホームページ  >  記事  >  バックエンド開発  >  Golang関数コルーチンプール実装技術共有

Golang関数コルーチンプール実装技術共有

王林
王林オリジナル
2023-05-16 08:31:522833ブラウズ

Golang は、迅速な開発と同時実行性の高い言語として、当然ながらコルーチン プールの実装も備えています。コルーチン プールはコルーチンの管理に使用されるデータ構造であり、コルーチンの総数を制限し、コルーチンの作成と破棄のタイミングを制御することで、同時環境でのリソースの使用を最適化できます。次に、Golang の関数を使用してコルーチン プールを実装する方法を紹介します。

  1. コルーチン プールの概念

コルーチン プールは、コルーチンの管理に使用されるデータ構造です。その目的は、コルーチンの数を制限し、コルーチンの作成と破棄のタイミングを調整し、プログラムの同時実行性を向上させます。

同時実行性が高い場合、コルーチンを開始すると毎回大量のオーバーヘッドが発生します。プログラムが同時に数百または数千のコルーチンを開く必要がある場合、これらのオーバーヘッドは非常に大きくなります。一般的な接続プールやスレッド プールと同様に、コルーチン プールはコンピューター リソースをより効率的に利用し、多数の同時操作を伴うタスクを完了できます。

  1. コルーチン プールの実装アイデア

コルーチン プールは、スケーラブル プールと固定プールに分類できます。このうち、スケーラブルプールは需要に応じて自動的に容量を拡張・縮小できるのに対し、固定プールは最初から容量が固定されており変更できません。

コルーチン プールを実装する Golang 関数の主なアイデアは、2 つのチャネルを通じて通信することです。 1 つは、コルーチン ワーカーにタスクを割り当てるために使用される workerChannel であり、もう 1 つは、workerChannel にタスクを渡すために使用されるタスク チャネルです。実行する必要があるタスクがある場合、そのタスクはタスク チャネルから取り出され、workerChannel 内の使用可能なワーカーの数に基づいてコルーチンが作成されるか、アイドル状態のワーカーにタスクが直接割り当てられて実行されます。タスクを完了したワーカーは、workerChannel に戻り、次のタスクの割り当てを待ちます。もちろん、場合によっては、タスクの実行方法を制御するために、コルーチン プールにミューテックスや待機グループなどのより多くのデータ構造を含めることもできます。

  1. コルーチン プールの実装コード

コルーチン プールを実装するための具体的なコードは次のとおりです:

package main

import (
    "fmt"
    "sync"
)

type Task struct {
    f func() error
}

var wg sync.WaitGroup

type Pool struct {
    //任务通道
    JobQueue chan Task
    //worker通道
    WorkerQueue chan chan Task
    //worker数量
    MaxWorkers int
}

func NewPool(maxWorkers int) *Pool {
    return &Pool{
        JobQueue:    make(chan Task, 10),
        WorkerQueue: make(chan chan Task, maxWorkers),
        MaxWorkers:  maxWorkers,
    }
}

func (p *Pool) Run() {
    for i := 0; i < p.MaxWorkers; i++ {
        worker := NewWorker(i+1, p.WorkerQueue)
        worker.Start()
    }

    go p.dispatch()
}

func (p *Pool) dispatch() {
    for {
        select {
        case job := <-p.JobQueue:
            fmt.Println("new job")
            worker := <-p.WorkerQueue
            fmt.Println("append job")
            worker <- job
            fmt.Println("after run job")
        }
    }
}

func (p *Pool) AddTask(task Task) {
    p.JobQueue <- task
}

type Worker struct {
    id          int
    WorkerQueue chan chan Task
    JobChannel  chan Task
    quitChan    chan struct{}
}

func NewWorker(id int, workerQueue chan chan Task) Worker {
    fmt.Println("newWorker")
    return Worker{
        id:          id,
        WorkerQueue: workerQueue,
        JobChannel:  make(chan Task),
        quitChan:    make(chan struct{}),
    }
}

func (w *Worker) Start() {
    fmt.Println("worker start")
    go func() {
        for {
            //将自己的jobChannel放入worker队列中
            w.WorkerQueue <- w.JobChannel
            select {
            case task := <-w.JobChannel:
                fmt.Printf("worker%d start job
", w.id)
                task.f()
                fmt.Printf("worker%d finished job
", w.id)
            case <-w.quitChan:
                fmt.Printf("worker%d quit
", w.id)
                return
            }
        }
    }()
}

func (w *Worker) Stop() {
    go func() {
        w.quitChan <- struct{}{}
    }()
}

func Hello() error {
    fmt.Println("Hello World")
    wg.Done()
    return nil
}

func main() {
    p := NewPool(5)
    p.Run()

    for i := 0; i < 100; i++ {
        task := Task{
            f: Hello,
        }
        wg.Add(1)
        p.AddTask(task)
    }
    wg.Wait()
}

上記のコードを実行すると、次のことがわかります。コントロール ステーションが出力するログ情報。このうち、ワーカー開始とは各ワーカーが実行を開始すること、新規ジョブとはタスクチャネルにタスクを追加すること、追加ジョブとはタスクがワーカーチャネルに配置されて実行を待機すること、実行後ジョブとはタスクが実行を開始することを意味します。正常に実行されました。

  1. コード分析

上記のコードでは、NewPool 関数を使用して、タスク チャネル、ワーカー チャネル、ワーカーの数を含むコルーチン プールを初期化します。 Worker タイプはコルーチン ワーカーに対応し、ワーカー コルーチンの実行を終了するタスク チャネルと終了チャネルが含まれます。 NewWorker 関数は、ワーカー オブジェクトを初期化し、そのタスク チャネルをコルーチン プール内のワーカー チャネルに追加する役割を果たします。

AddTask 関数は、コルーチン プール タスク チャネルに新しいタスクを追加するために使用されます。この関数は、タスクが追加されるまでブロックします。ワーカー チャネルに空きワーカーがある場合、タスクはそのワーカーに直接割り当てられます。そうでない場合は、ワーカー チャネル内のワーカーが解放されるまで待機します。

Start 関数は、ワーカー コルーチンを開始し、タスクの到着の待機を開始する役割を果たします。この関数は、まず独自のタスク チャネルをワーカー チャネルに追加し、次にタスク チャネルが閉じるかチャネル終了信号を受信するまでタスクの到着を待ちます。タスクを受信した場合は、タスクを実行します。ループ内でチャネル終了信号を受信した場合、コルーチンの実行を終了する必要があることを意味し、この時点でワーカーはワーカー チャネルから自身を削除します。

ディスパッチ関数は、タスク チャネルをリッスンし、利用可能なワーカーに基づいてタスクをチャネルに割り当てる go コルーチンです。タスク チャネルに新しいタスクがある場合、ディスパッチはワーカー チャネルから空きワーカーを取得し、それらにタスクを割り当てようとします。ワーカー チャネルに空きワーカーがない場合、ワーカーが解放されるまで待機します。

  1. 概要

この記事では、コルーチン プールを実装する Golang 関数の考え方と実装コードを紹介します。コルーチンの数はコルーチン プールを通じて制御できるため、コンピューター リソースが最大限に活用され、高同時実行環境でのプログラムの同時実行性が向上します。

以上がGolang関数コルーチンプール実装技術共有の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。