Golang WorkerPool线程池并发模式示例详解

Golang WorkerPool线程池并发模式示例详解

简介

WorkerPool即工作池,也称为线程池。它是一种并发编程模式,通常用于解决并发问题。在WorkerPool中,创建固定数量的worker,他们并行地从池中获取任务,并在处理任务时将其标记为完成。当所有可用的Worker都在使用时,新任务将被放入队列中,并等待有空闲的Worker。

原理

WorkerPool的主要优点是控制线程达到最大效率,最大限度地利用CPU时间,由于每个Worker都支持并发,因此可以处理大量的任务,并且可以在应用程序中实现流控制。以下是WorkerPool的基本原理:

  • 创建一个worker池,该池中包含了可以进行并发处理的固定数量的worker。

  • 工人从池中获取任务并执行任务。

  • 当处理完成任务时,工作人员将结果放回池中。

  • 如果池中没有可用的worker,则将任务放入队列中,并等待worker可用时将其分配。

示例一

在此示例中,将展示如何在Golang中实现WorkerPool模式。此示例将创建两个worker并向池中添加任务。

package main

import (
    "time"
    "fmt"
)

type job struct {
    id       int
    randomno int
}

type result struct {
    job         job
    sumofdigits int
}

var jobs = make(chan job, 10)
var results = make(chan result, 10)

func digits(number int) int {
    sum := 0
    no := number
    for no != 0 {
        digit := no % 10
        sum += digit
        no /= 10
    }
    time.Sleep(2 * time.Second)
    return sum
}

func worker(w int) {
    for job := range jobs {
        output := result{job, digits(job.randomno)}
        results <- output
    }
}

func createWorkerPool(noOfWorkers int) {
    for i := 0; i < noOfWorkers; i++ {
        go worker(i)
    }
}

func allocate(noOfJobs int) {
    for i := 0; i < noOfJobs; i++ {
        randomno := 99
        job := job{i, randomno}
        jobs <- job
    }
    close(jobs)
}

func result(done chan bool) {
    for result := range results {
        fmt.Printf("job id %d, input random no %d, sum of digits %d\n", result.job.id, result.job.randomno, result.sumofdigits) 
    }
    done <- true
}

func main() {
    startTime := time.Now()
    noOfJobs := 10
    go allocate(noOfJobs)
    done := make(chan bool)
    go result(done)
    noOfWorkers := 2
    createWorkerPool(noOfWorkers)
    <-done
    endTime := time.Now()
    diffTime := endTime.Sub(startTime)
    fmt.Println("total time taken ", diffTime.Seconds(), "seconds")
}

运行后的输出结果为

job id 0, input random no 99, sum of digits 18
job id 4, input random no 99, sum of digits 18
job id 2, input random no 99, sum of digits 18
job id 3, input random no 99, sum of digits 18
job id 1, input random no 99, sum of digits 18
job id 6, input random no 99, sum of digits 18
job id 5, input random no 99, sum of digits 18
job id 8, input random no 99, sum of digits 18
job id 7, input random no 99, sum of digits 18
job id 9, input random no 99, sum of digits 18
total time taken  2.156952 seconds

示例二

在此示例中,将演示如何创建带有worker-pool的HTTP服务器。

package main

import (
    "fmt"
    "io/ioutil"
    "net/http"
)

var jobs = make(chan string, 10)
var results = make(chan string, 10)

func worker(w int) {
    for job := range jobs {
        resp, err := http.Get(job)
        if err == nil {
            bodyBytes, err := ioutil.ReadAll(resp.Body)
            if err == nil {
                results <- fmt.Sprintf("Worker id %d requested url %s and status code %s length is %d" , w, job, resp.Status, len(bodyBytes))
            }
        } else {
            results <- fmt.Sprintf("Worker id %d requested url %s but received error %v", w, job, err)
        }
    }
}

func createWorkerPool(noOfWorkers int) {
    for i := 0; i < noOfWorkers; i++ {
        go worker(i)
    }
}

func allocate(w http.ResponseWriter, req *http.Request) {
    url := req.URL.Query().Get("url")
    if url == "" {
        http.Error(w, "Please provide a URL", http.StatusBadRequest)
        return
    }
    jobs <- url
    w.WriteHeader(http.StatusCreated)
    return
}

func result(w http.ResponseWriter, req *http.Request) {
    w.Header().Set("Content-Type", "text/event-stream")
    w.Header().Set("Cache-Control", "no-cache")
    w.Header().Set("Connection", "keep-alive")
    w.WriteHeader(http.StatusOK)
    flusher := w.(http.Flusher)
    for {
        select {
        case result := <-results:
            fmt.Fprintf(w, "data: {\"message\": \"%s\"}\n\n", result)
            flusher.Flush()
        }
    }
}

func main() {
    noOfWorkers := 2
    createWorkerPool(noOfWorkers)
    http.HandleFunc("/allocate", allocate)
    http.HandleFunc("/result", result)
    http.ListenAndServe(":8080", nil)
}

运行示例后,在浏览器中打开http://localhost:8080/allocate?url=http://www.google.com,会在控制台上看到worker完成了请求和响应,像这样:

Worker id 1 requested url http://www.google.com and status code 200 OK length is 11662

结论

Go语言支持WorkerPool线程池并发模式,使应用程序更加高效。希望以上示例可以为你提供一些灵感和帮助。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Golang WorkerPool线程池并发模式示例详解 - Python技术站

(0)
上一篇 2023年5月17日
下一篇 2023年5月17日

相关文章

  • python多线程操作实例

    让我来为你详细讲解一下“Python多线程操作实例”的完整攻略。 Python多线程操作实例 多线程操作是提高Python程序运行速度和效率的关键技术之一。多线程是指一个进程中的多个线程同时执行独立任务的能力,这些线程可以并发执行或同时运行。 在Python中,我们可以使用threading模块来实现多线程编程。下面我将为你介绍Python多线程操作的实例和…

    多线程 2023年5月17日
    00
  • 使用lua+redis解决发多张券的并发问题

    下面我详细讲解一下使用Lua+Redis解决发多张券的并发问题的攻略。 什么是发多张券的并发问题 发多张券的并发问题是指当多个用户同时请求获取优惠券时,可能会造成出现超卖的情况,即券码数量不足,统一券码被领取数超过了预设数量。这种问题在高并发场景下尤为常见。 解决方案 一种常见的解决方案是使用分布式锁,但是这种方案不够优雅,因为它需要多次请求获取锁,而且需要…

    多线程 2023年5月16日
    00
  • Java多线程run方法中直接调用service业务类应注意的问题及解决

    下面是关于“Java多线程run方法中直接调用service业务类应注意的问题及解决”的完整攻略: 问题描述 在Java的多线程程序中,run方法中直接调用service业务类可能会带来以下问题: 业务逻辑的复杂度增加,使得程序难以维护和扩展; 可能会导致死锁或同步问题,因为run方法本身就是在一个线程中执行的,如果在其中调用service方法,可能会导致与…

    多线程 2023年5月16日
    00
  • java线程池合理设置最大线程数和核心线程数方式

    下面是Java线程池合理设置最大线程数和核心线程数的完整攻略: 1. 什么是线程池以及为什么要使用线程池 线程池是一种多线程编程的技术,它可以通过复用已经创建好的线程来处理新的任务,从而降低线程实例的创建和销毁所带来的开销。使用线程池可以优化多线程应用程序的性能,防止在系统资源有限的情况下过度创建线程,导致系统性能下降,甚至崩溃。 2. 如何合理设置线程池的…

    多线程 2023年5月16日
    00
  • Linux Shell多进程并发以及并发数控制

    想要实现Linux Shell多进程并发以及并发数控制,可以使用一些经典的工具和技巧。 第一个工具就是xargs,它能够从标准输入中读取参数并将其转换成命令行参数。可以使用-P参数指定一个进程池的大小,从而控制同时运行的进程数。例如: $ find . -name "*.png" | xargs -P 4 -I{} file {} 这个命…

    多线程 2023年5月16日
    00
  • Java Lambda表达式原理及多线程实现

    下面是对于“Java Lambda表达式原理及多线程实现”的完整攻略。 什么是Lambda表达式 Lambda表达式是Java 8引入的一个新特性,它主要是为了简化一个接口(或者抽象类)的实现,从而使得代码更加简洁易读。Lambda表达式的本质是一个匿名函数,它没有名称,但是具备参数列表和方法体。 Lambda表达式有如下的语法格式: (parameters…

    多线程 2023年5月17日
    00
  • GoLang并发机制探究goroutine原理详细讲解

    GoLang并发机制探究goroutine原理详细讲解 什么是goroutine goroutine 是Go语言中的一种轻量级线程,能够在用户态(User Space)进行创建和销毁,不需要操作系统提供的线程管理和调度,因此比传统线程的创建、销毁和轮转开销更小,同时能够高效地利用多核CPU性能。 Go语言中的协程(goroutine)有着更加灵活的调度和更少…

    多线程 2023年5月17日
    00
  • SpringBoot项目的多文件兼多线程上传下载

    下面我将详细讲解SpringBoot项目的多文件兼多线程上传下载的完整攻略。 1. 多文件上传 1.1 前端页面实现 第一步是实现前端页面,让用户可以选择并上传多个文件。在html文件中,使用<input type=”file” multiple>标签实现多个文件上传,代码如下: <form action="/upload&quo…

    多线程 2023年5月16日
    00
合作推广
合作推广
分享本页
返回顶部