web-dev-qa-db-ja.com

goroutineプールの使用方法

Yahooファイナンスから株価スプレッドシートをダウンロードするためにGoを使用したいと思います。独自のgoroutineですべての株に対してhttpリクエストを作成します。約2500のシンボルのリストがありますが、2500のリクエストを並行して行うのではなく、一度に250を作成することをお勧めします。 Javaで、私はスレッドプールを作成し、スレッドが解放されたときにスレッドを再利用します。似たようなものを見つけようとしていたのですが、可能であれば、ルーチンはありませんリソース。誰かが手元にあるタスクを実行する方法を教えてもらえますか、または同じようにリソースを私に示すことができれば幸いです。ありがとう!

31
tldr

最も簡単な方法は、250個のゴルーチンを作成し、メインのゴルーチンから子のゴルーチンにリンクを渡して、そのチャネルをリッスンするために使用できるチャネルを渡すことだと思います。

すべてのリンクがゴルーチンに渡されると、チャネルを閉じ、すべてのゴルーチンがジョブを終了します。

子がデータを処理する前にメインのゴルーチンが終了しないようにするには、sync.WaitGroupを使用できます。

これは私が上で言った(最終的な作業バージョンではなくポイントを示す)説明するコードです。

func worker(linkChan chan string, wg *sync.WaitGroup) {
   // Decreasing internal counter for wait-group as soon as goroutine finishes
   defer wg.Done()

   for url := range linkChan {
     // Analyze value and do the job here
   }
}

func main() {
    lCh := make(chan string)
    wg := new(sync.WaitGroup)

    // Adding routines to workgroup and running then
    for i := 0; i < 250; i++ {
        wg.Add(1)
        go worker(lCh, wg)
    }

    // Processing all links by spreading them to `free` goroutines
    for _, link := range yourLinksSlice {
        lCh <- link
    }

    // Closing channel (waiting in goroutines won't continue any more)
    close(lCh)

    // Waiting for all goroutines to finish (otherwise they die as main routine dies)
    wg.Wait()
}
48

これからGoのスレッドプール実装ライブラリを使用できます git repo

ここ は、チャネルをスレッドプールとして使用する方法に関する素晴らしいブログです。

ブログの抜粋

    var (
 MaxWorker = os.Getenv("MAX_WORKERS")
 MaxQueue  = os.Getenv("MAX_QUEUE")
)

//Job represents the job to be run
type Job struct {
    Payload Payload
}

// A buffered channel that we can send work requests on.
var JobQueue chan Job

// Worker represents the worker that executes the job
type Worker struct {
    WorkerPool  chan chan Job
    JobChannel  chan Job
    quit        chan bool
}

func NewWorker(workerPool chan chan Job) Worker {
    return Worker{
        WorkerPool: workerPool,
        JobChannel: make(chan Job),
        quit:       make(chan bool)}
}

// Start method starts the run loop for the worker, listening for a quit channel in
// case we need to stop it
func (w Worker) Start() {
    go func() {
        for {
            // register the current worker into the worker queue.
            w.WorkerPool <- w.JobChannel

            select {
            case job := <-w.JobChannel:
                // we have received a work request.
                if err := job.Payload.UploadToS3(); err != nil {
                    log.Errorf("Error uploading to S3: %s", err.Error())
                }

            case <-w.quit:
                // we have received a signal to stop
                return
            }
        }
    }()
}

// Stop signals the worker to stop listening for work requests.
func (w Worker) Stop() {
    go func() {
        w.quit <- true
    }()
} 
2
Shettyh

この例では、2つのチャネルを使用します。1つは入力用で、もう1つは出力用です。ワーカーは任意のサイズにスケーリングでき、各ゴルーチンは入力キューで機能し、すべての出力を出力チャネルに保存します。より簡単な方法に関するフィードバックは大歓迎です。

package main

import (
    "fmt"
    "sync"
)

var wg sync.WaitGroup

func worker(input chan string, output chan string) {
    defer wg.Done()
    // Consumer: Process items from the input channel and send results to output channel
    for value := range input {
        output <- value + " processed"
    }
}

func main() {
    var jobs = []string{"one", "two", "three", "four", "two", "three", "four", "two", "three", "four", "two", "three", "four", "two", "three", "four", "two"}
    input := make(chan string, len(jobs))
    output := make(chan string, len(jobs))
    workers := 250

    // Increment waitgroup counter and create go routines
    for i := 0; i < workers; i++ {
        wg.Add(1)
        go worker(input, output)
    }

    // Producer: load up input channel with jobs
    for _, job := range jobs {
        input <- job
    }

    // Close input channel since no more jobs are being sent to input channel
    close(input)
    // Wait for all goroutines to finish processing
    wg.Wait()
    // Close output channel since all workers have finished processing
    close(output)

    // Read from output channel
    for result := range output {
        fmt.Println(result)
    }

}
2
OkezieE