一、Go基础知识27、多路选择和超时、channel关闭和广播、任务的取消与Context

一、多路选择和超时

在Go语言中,多路选择和超时通常与select语句结合使用,用于同时监听多个通道的操作,并支持超时机制。

1、多路选择(select)

select语句允许在多个通道操作中选择一个可执行的操作。每个case语句表示一个通道操作。

示例

func main() {
    ch1 := make(chan string)
    ch2 := make(chan string)

    go func() {
        time.Sleep(time.Second)
        ch1 <- "Data from ch1"
    }()

    go func() {
        time.Sleep(2 * time.Second)
        ch2 <- "Data from ch2"
    }()

    select {
    case msg1 := <-ch1:
        fmt.Println("Received from ch1:", msg1)
    case msg2 := <-ch2:
        fmt.Println("Received from ch2:", msg2)
    }
}

解释
在这个例子中,select语句会等待ch1ch2中的任意一个通道准备好,并执行相应的操作。在实际应用中,可以用select来监听多个通道,响应先准备好的通道。

2、超时机制

超时机制用于避免程序永远等待某个操作完成。可以使用time.Afterselect结合实现超时。

示例

func main() {
    ch := make(chan string)

    go func() {
        time.Sleep(2 * time.Second)
        ch <- "Data from ch"
    }()

    select {
    case msg := <-ch:
        fmt.Println("Received:", msg)
    case <-time.After(1 * time.Second):
        fmt.Println("Timeout: Operation took too long")
    }
}

解释
在这个例子中,select语句等待ch通道的操作,但是如果1秒内没有接收到数据,就会执行time.After中的操作,即超时处理。

3、多路选择与超时结合

func main() {
    ch1 := make(chan string)
    ch2 := make(chan string)

    go func() {
        time.Sleep(time.Second)
        ch1 <- "Data from ch1"
    }()

    go func() {
        time.Sleep(2 * time.Second)
        ch2 <- "Data from ch2"
    }()

    select {
    case msg1 := <-ch1:
        fmt.Println("Received from ch1:", msg1)
    case msg2 := <-ch2:
        fmt.Println("Received from ch2:", msg2)
    case <-time.After(3 * time.Second):
        fmt.Println("Timeout: No data received in 3 seconds")
    }
}

解释
在这个例子中,select语句会等待ch1ch2中的任意一个通道准备好,或者等待3秒超时。如果3秒内没有任何通道准备好,就会执行超时处理。

另外
多路选择和超时结合使用使得程序能够更灵活地处理并发操作,确保不会因为某个操作长时间未完成而导致程序阻塞。

Go语言中的channel是一种用于在goroutine之间传递数据的通信机制。Channel的关闭和广播是两个与channel相关的重要操作。

二、Channel关闭和广播

1、Channel的关闭

1.1、关闭Channel的目的

关闭一个channel的主要目的是通知接收方没有更多的数据要接收了,以防止接收方阻塞等待数据。

1.2、关闭Channel的语法

使用内建的close函数可以关闭一个channel,例如:

close(myChannel)

1.3、关闭后的影响

一旦一个channel被关闭,任何尝试向其发送数据的操作都会引发panic,但接收操作仍然可以接收之前已经发送的数据。

1.4、检测Channel是否关闭

使用多重返回值的方式检测channel是否已经关闭,例如:

data, ok := <-myChannel
if !ok {
    // Channel已关闭
}
  1. 例子:
    package main
    
    import "fmt"
    
    func main() {
        myChannel := make(chan int, 3)
    
        go func() {
            for i := 0; i < 3; i++ {
                myChannel <- i
            }
            close(myChannel)
        }()
    
        for num := range myChannel {
            fmt.Println(num)
        }
    }
    

解释
在这个例子中,goroutine向channel发送了三个数据,然后关闭了channel,主goroutine通过range循环接收这些数据,一旦channel关闭,循环会退出。

2、Channel的广播

2.1、广播的概念

在Go中,我们可以使用channel来实现广播,即向多个goroutine同时发送相同的消息。

2.2、使用单向channel实现广播

可以使用只写或只读的channel来实现广播,例如:

package main

import "fmt"

func main() {
    messageChannel := make(chan string)
    done := make(chan bool)

    // 发送广播
    go func() {
        for i := 0; i < 3; i++ {
            messageChannel <- fmt.Sprintf("Message %d", i)
        }
        close(messageChannel)
        done <- true
    }()

    // 接收广播
    go func() {
        for msg := range messageChannel {
            fmt.Println(msg)
        }
        done <- true
    }()

    // 等待两个goroutine完成
    <-done
    <-done
}

解释
在这个例子中,一个goroutine发送了三个消息到channel,另一个goroutine通过range循环接收这些消息,最后两个goroutine通过done channel通知主goroutine它们已经完成。

注意
这样的实现可以确保多个goroutine同时接收相同的消息,实现了一种简单的广播机制。
需要注意的是,这里使用了两个done channel来确保两个goroutine都完成了它们的任务。

三、任务的取消

Go语言的并发编程中,任务的取消是一个重要的话题,特别是在处理长时间运行的任务时,需要考虑如何优雅地取消这些任务。

1、取消的需求

在并发编程中,有时候我们需要取消一个正在执行的任务,可能是因为超时、用户请求取消或者其他原因。

2、使用context

Go语言提供了context包来处理任务的取消和超时问题。context包提供了一种在goroutine之间传递取消信号的机制。

3、创建带取消功能的Context

可以使用context.WithCancel函数创建一个带有取消功能的Context,例如:

ctx, cancel := context.WithCancel(context.Background())
defer cancel() // 通常在函数结束时调用cancel函数

4、取消任务

调用cancel函数可以取消与该Context相关的任务,例如:
go cancel() // 取消与ctx相关的任务

5、检测取消信号

可以通过ctx.Done()通道来检测是否收到了取消信号,例如:
go select { case <-ctx.Done(): // 收到取消信号,执行相应的处理 }

6、示例

package main

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

func longRunningTask(ctx context.Context) {
    for {
        select {
        case <-ctx.Done():
            fmt.Println("Task cancelled")
            return
        default:
            fmt.Println("Task is running")
            time.Sleep(1 * time.Second)
        }
    }
}

func main() {
    ctx, cancel := context.WithCancel(context.Background())
    defer cancel()

    go longRunningTask(ctx)

    time.Sleep(3 * time.Second)
    cancel() // 取消任务
    time.Sleep(1 * time.Second) // 等待任务退出
}

解释
在这个例子中,longRunningTask是一个长时间运行的任务,通过context包实现了任务的取消功能。在main函数中,我们创建了一个带取消功能的Context,并启动了一个goroutine来执行长时间任务,然后在3秒后取消了这个任务。

  • 22
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

风不归Alkaid

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值