c语言编程笔录

首页 >   > 笔记大全

笔记大全

Go语言并发任务调度解决方法

更新时间:2023-11-06

Go语言并发任务调度解决方法

在并发编程中,任务调度是非常关键的一环。Go语言作为一种支持并发的编程语言,在任务调度方面也做得相当不错。下面我们来介绍一些Go语言并发任务调度解决方法。

1. Goroutine

Goroutine是Go语言中的轻量级线程,它能够在主线程运行的同时执行任务。通过Goroutine,我们可以充分利用CPU资源,使程序的性能得到很大程度的提升。

下面是一个使用Goroutine实现任务调度的示例代码:

package main

import (
    "fmt"
)

func worker(id int, jobs <-chan int, results chan<- int) {
    for j := range jobs {
        fmt.Println("worker", id, "processing job", j)
        results <- j * 2
    }
}

func main() {
    jobs := make(chan int, 100)
    results := make(chan int, 100)

    for w := 1; w <= 3; w++ {
        go worker(w, jobs, results)
    }

    for j := 1; j <= 9; j++ {
        jobs <- j
    }
    close(jobs)

    for a := 1; a <= 9; a++ {
        <-results
    }
}

上面这段代码中,我们创建了一个可以缓存100个整数的jobs通道和一个可以缓存100个整数的results通道。我们还创建了三个Goroutine去执行真正的任务,即worker函数。

在main函数中,我们往jobs通道中放入了1到9的整数,然后通过for循环取出从results通道中取出任务结果。由于我们创建了三个Goroutine,并且jobs缓存大小为100,因此可以安全地并发执行9个任务,从而达到了任务调度的目的。

2. Channel

除了Goroutine,Channel也是Go语言并发编程中的一个核心概念。Channel可以用于Goroutine之间的通信和同步,也可以用于任务调度。

以下是一个通过Channel实现任务调度的示例代码:

package main

import (
    "fmt"
)

func worker(id int, jobs chan int, results chan int) {
    for j := range jobs {
        fmt.Println("worker", id, "processing job", j)
        results <- j * 2
    }
}

func main() {
    jobCount := 9
    jobs := make(chan int, jobCount)
    results := make(chan int, jobCount)

    for w := 1; w <= 3; w++ {
        go worker(w, jobs, results)
    }

    for j := 1; j <= jobCount; j++ {
        jobs <- j
    }
    close(jobs)

    for a := 1; a <= jobCount; a++ {
        <-results
    }
}

在上面这段代码中,我们依旧创建了jobs和results两个通道,用于传递任务和任务结果。worker函数和之前的示例代码类似,只不过它接收的不是jobs缓存数组,而是jobs通道。

在main函数中,我们仍然使用了jobs缓存数组来存储任务,不过这次我们通过jobs通道将任务分发给不同的Goroutine进行执行。

3. WaitGroup

WaitGroup是Go语言中另一个常用的并发编程工具,可以用于同步多个Goroutine的执行。对于任务调度来说,我们可以通过WaitGroup的方式来等待所有任务执行完成。

package main

import (
    "fmt"
    "sync"
)

func worker(id int, jobs chan int, results chan<- int, wg *sync.WaitGroup) {
    defer wg.Done()

    for j := range jobs {
        fmt.Println("worker", id, "processing job", j)
        results <- j * 2
    }
}

func main() {
    jobCount := 9
    jobs := make(chan int, jobCount)
    results := make(chan int, jobCount)

    var wg sync.WaitGroup

    for w := 1; w <= 3; w++ {
        wg.Add(1)
        go worker(w, jobs, results, &wg)
    }

    for j := 1; j <= jobCount; j++ {
        jobs <- j
    }
    close(jobs)

    wg.Wait()

    for a := 1; a <= jobCount; a++ {
        <-results
    }
}

在上面这个示例代码中,我们添加了sync.WaitGroup,并在worker函数中使用了defer wg.Done()来表示任务已完成。在main函数中,我们使用wg.Add(1)将worker的数量加入wait group中,用于等待所有任务执行完成。

4. Context

Context是Go语言中较新的并发编程工具,它可以用于控制多个Goroutine的执行。在任务调度中,Context可以用于控制任务的超时、取消等,从而有效地保证程序的健壮性。

package main

import (
    "context"
    "fmt"
    "time"
)

func worker(ctx context.Context, id int, jobs chan int, results chan int) {
    for {
        select {
        case <-ctx.Done():
            return
        case j, ok := <-jobs:
            if ok {
                fmt.Println("worker", id, "processing job", j)
                results <- j * 2
            } else {
                return
            }
        }
    }
}

func main() {
    jobCount := 9
    jobs := make(chan int, jobCount)
    results := make(chan int, jobCount)

    ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
    defer cancel()

    for w := 1; w <= 3; w++ {
        go worker(ctx, w, jobs, results)
    }

    for j := 1; j <= jobCount; j++ {
        jobs <- j
    }
    close(jobs)

    for a := 1; a <= jobCount; a++ {
        select {
        case <-time.After(500 * time.Millisecond):
            fmt.Println("timeout")
        case r := <-results:
            fmt.Println("result:", r)
        }
    }
}

在上面这段代码中,我们使用了Context来控制任务的执行。我们使用了WithTimeout方法来将任务执行时间限制为1秒钟。在worker函数中,我们使用select和context.Done()来控制任务的执行。

由于我们将任务执行时间限制为1秒钟,并且在main函数中使用了select来等待任务执行完成,因此我们可以避免雪崩效应的发生,从而保证程序的健壮性。

总结

在本文中,我们介绍了四种Go语言并发任务调度解决方法,包括Goroutine,Channel,WaitGroup和Context。这些工具非常适合用于并发编程和任务调度,在实际开发中应用广泛。

在使用这些工具时,我们需要根据具体情况选择合适的工具,并合理地处理好并发操作。只有这样,我们才能写出高效、健壮的并发程序,在保证程序性能和稳定性的同时,提高程序开发效率。