go并发编程系列六:线程分组及控制线程的合作执行

背景:线程的合作执行,体现的是团结协作,应该是比较理想的状态,如果人人都能够少一些算计、多一点互帮互助,那该有多好啊?班主任不是资本家,班级更应该提倡团队精神,学生之间不应该竞争,应该互相协作!本文以团结协作为出发点,讲解线程的合作执行。

为了适应线程协作的场景,我们需要先搞明白下面的几个关键点:

1、怎样启动线程

2、任务用什么样的格式保存

3、任务全部认领完成以后,怎样关闭线程

作为班主任,有一个植树活动的新任务,需要班级3位同学共同完成,要求:每位同学随机领取0个以上的任务。

一、启动3个线程,用于模拟3个学生

go func() {
    fmt.Println("张三")
}()

go func() {
    fmt.Println("李四")
}()

go func() {
  fmt.Println("王五")  
}()

 

二、定义1个用于保存任务的容器

type Task struct {
    mu         sync.Mutex
    items    []string
}

func NewTask() *Task {
    return &Task{}
}

func (t *Task) Add(item string)  {
    t.mu.Lock()
    defer t.mu.Unlock()

    t.items = append(t.items, item)
}

func (t *Task) Remove() string {
    t.mu.Lock()
    defer t.mu.Unlock()

    if len(t.items) == 0 {
        return ""
    }

    index := rand.Intn(len(t.items))
    item := t.items[index]
    t.items = append(t.items[:index], t.items[index+1:]...)
    return item
}

三、让线程消费容器里的任务,容器空的时候,表示任务已经认领完毕

这里我们用到了context.WithCancel,通俗的讲,这是一组带有取消开关的信道组的Context,执行取消后,这一组信道会收到Done信号,我们把这3个线程视为一组,用这个Context进行承载,这样,在学生认领完容器的所有任务后,通过取消功能,就可以结束掉所有线程。

package main

import (
    "context"
    "fmt"
    "math/rand"
    "strings"
    "sync"
    "time"
)

type Task struct {
    mu         sync.Mutex
    items    []string
}

func NewTask() *Task {
    return &Task{}
}

func (t *Task) Add(item string)  {
    t.mu.Lock()
    defer t.mu.Unlock()

    t.items = append(t.items, item)
}

func (t *Task) Remove() string {
    t.mu.Lock()
    defer t.mu.Unlock()

    if len(t.items) == 0 {
        return ""
    }

    index := rand.Intn(len(t.items))
    item := t.items[index]
    t.items = append(t.items[:index], t.items[index+1:]...)
    return item
}

func main() {
    task := NewTask()
    task.Add("买树苗")
    task.Add("挖坑")
    task.Add("栽树苗")
    task.Add("回填")
    task.Add("培土")
    task.Add("施肥")
    task.Add("浇水")

    var wg sync.WaitGroup
    ctx, cancel := context.WithCancel(context.Background())

    wg.Add(3)

    go func() {
        defer wg.Done()

        for {
            select {
            case <- ctx.Done():
                fmt.Println("张三:退出")
                return
            default:
                item := task.Remove()
                if !IsEmptyString(item) {
                    fmt.Printf("张三:%s\n", item)
                }
                time.Sleep(time.Second)
            }
        }
    }()

    go func() {
        defer wg.Done()

        for {
            select {
            case <- ctx.Done():
                fmt.Println("李四:退出")
                return
            default:
                item := task.Remove()
                if !IsEmptyString(item) {
                    fmt.Printf("李四:%s\n", item)
                }
                time.Sleep(time.Second)
            }
        }
    }()

    go func() {
        defer wg.Done()

        for {
            select {
            case <- ctx.Done():
                fmt.Println("王五:退出")
                return
            default:
                item := task.Remove()
                if !IsEmptyString(item) {
                    fmt.Printf("王五:%s\n", item)
                }
                time.Sleep(time.Second)
            }
        }
    }()

    time.Sleep(3 * time.Second)
    cancel()

    wg.Wait()
}

func IsEmptyString(str string) bool {
    trimmedStr := strings.TrimSpace(str)
    return trimmedStr == ""
}

 

 来看下,运行的效果:

 

 

posted @ 2023-09-02 17:20  jamstack  阅读(13)  评论(0编辑  收藏  举报