Golang开发--channel的使用

这篇具有很好参考价值的文章主要介绍了Golang开发--channel的使用。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

在 Go 语言中,channel(通道)是一种用于在 goroutine 之间进行通信和同步的并发原语。它提供了一种安全且简单的方式来传递数据。

通道的详细描述和使用方法

1.定义通道:
通道是通过使用 make 函数来创建的。通道有特定的类型,用于指定通道中传输的数据的类型。例如,ch := make(chan int) 创建了一个整型数据传输的通道。

2.发送和接收数据:
使用 <- 运算符可以将数据发送到通道或从通道接收数据。发送操作将数据发送到通道中,接收操作从通道中接收数据。例如,ch <- data 将数据 data 发送到通道 ch 中,result := <- ch 从通道 ch 中接收数据,并将其存储在变量 result 中。

3.阻塞和同步:
通道提供了同步的机制,当发送或接收操作发生时,它们可以阻塞当前的 goroutine。发送操作在通道已满时会阻塞,直到有其他 goroutine 从通道中接收数据。接收操作在通道为空时会阻塞,直到有其他 goroutine 向通道发送数据。这种阻塞机制可以用于确保 goroutine 之间的同步。

4.关闭通道:
可以使用 close 函数关闭通道。关闭通道后,任何接收操作都会立即完成,并返回通道中剩余的数据。对已关闭的通道进行发送操作会引发 panic。可以使用多返回值来判断通道是否已关闭,例如,data, ok := <- ch,其中 ok 的值为 false 表示通道已关闭。
当一个通道被关闭后,仍然可以继续读取通道中的数据,直到通道中的数据全部被读取完毕。关闭通道只是一个标识,表示不会再有新的数据被发送到通道中。

5.通道的容量和阻塞:
通道可以有一个可选的容量,用于限制可以在通道中存储的元素数量。通道(channel)可以分为有缓存通道和无缓存通道,它们在用法和行为上有一些区别。

无缓存通道(Unbuffered Channel):

无缓存通道是指通道的容量为 0,也称为同步通道。
当向无缓存通道发送数据时,发送操作会阻塞,直到有其他协程从该通道接收数据。
当从无缓存通道接收数据时,接收操作也会阻塞,直到有其他协程向该通道发送数据。
无缓存通道的发送和接收操作会导致发送方和接收方同步等待,即发送操作和接收操作在双方都准备好之前都不会完成。
无缓存通道可以用于实现协程之间的同步和数据传递,确保发送和接收操作的顺序和时机。

ch := make(chan int)  // 创建一个无缓存通道

有缓存通道(Buffered Channel):

有缓存通道是指通道具有指定的容量,容量大于 0。
当向有缓存通道发送数据时,如果通道的缓冲区未满,发送操作会立即完成,而不会阻塞发送方。
当从有缓存通道接收数据时,如果通道的缓冲区非空,接收操作会立即完成,而不会阻塞接收方。
当通道的缓冲区已满时,发送操作会阻塞发送方,直到有其他协程从通道中接收数据并腾出空间。
当通道的缓冲区为空时,接收操作会阻塞接收方,直到有其他协程向通道发送数据。
有缓存通道可以用于实现异步的数据传递,发送方和接收方不需要同时准备好,只要缓冲区未满或非空即可。

ch := make(chan int, 5)  // 创建一个容量为 5 的有缓存通道

需要注意的是,无论是有缓存通道还是无缓存通道,在协程之间进行通信时,发送和接收操作都会阻塞协程。这种阻塞操作可以帮助协程之间同步和协调,并确保数据的正确传递。

6.使用 select 语句:
select 语句可以用于在多个通道之间进行非阻塞的选择操作。它可以监听多个通道的发送和接收操作,并执行第一个准备就绪的操作。select语句可以与case子句一起使用,每个case` 子句对应一个通道操作。

通道是 Go 语言中实现并发通信的重要机制之一。它们是线程安全的,可以安全地在多个 goroutine 之间传递数据,并提供了简单而有效的同步机制。通过合理使用通道,可以实现高效的并发编程。

代码示例

阻塞和同步:
通道的阻塞特性可用于实现同步。例如,当一个 goroutine 需要等待另一个 goroutine 完成某些操作时,可以使用通道来进行同步。下面是一个简单的示例:

package main

import "fmt"

func worker(done chan bool) {
	fmt.Println("正在执行工作...")
	// 模拟工作耗时
	// ...

	fmt.Println("工作完成")
	done <- true // 发送完成信号到通道
}

func main() {
	done := make(chan bool) // 创建通道

	go worker(done) // 启动工作协程

	<-done // 等待工作完成的信号
	fmt.Println("主程序结束")
}

这行代码使用go关键字启动了一个新的goroutine,调用了worker函数,并将done通道作为参数传递给worker函数。这意味着worker函数将在独立的goroutine中执行,与主函数的执行是并发的。

<-done // 等待工作完成的信号

这行代码使用接收操作<-done从done通道中接收数据。它会阻塞主goroutine的执行,直到从done通道接收到数据。在这里,我们等待worker函数执行完成并向done通道发送完成信号。

通过这样的设计,我们实现了主goroutine与工作协程之间的同步。主goroutine在<-done这一行代码处等待,直到工作协程执行完毕并向done通道发送完成信号,主goroutine才会继续执行后续的代码。

这种方式可以确保在工作协程完成之前,主goroutine不会提前结束,从而实现了协程之间的同步和协作。

需要注意的是,通道是一种用于在goroutine之间进行通信和同步的重要机制。通过发送和接收操作,可以实现goroutine之间的信息交换和控制流程的同步。在这个例子中,done通道被用作一个信号通道,用于通知主goroutine工作协程的完成状态。

通道的迭代:
可以在 for 循环中使用通道进行迭代,直到通道关闭。这样可以便捷地处理通道中的元素。下面是一个示例:

package main

import "fmt"

func main() {
	nums := []int{2, 4, 6, 8, 10}

	ch := make(chan int)

	go func() {
		for _, num := range nums {
			ch <- num // 发送元素到通道
		}
		close(ch) // 关闭通道
	}()

	for num := range ch {
		fmt.Println(num) // 从通道接收元素并打印
	}
}

在上述示例中,我们将整数切片中的元素发送到通道 ch 中。然后,使用 range 循环从通道 ch 中接收元素,并打印每个元素的值。当通道关闭后,range 循环会自动退出。

使用 select 语句:
select语句可以同时监听多个通道的操作,执行第一个准备就绪的操作。下面是一个示例:

package main

import (
	"fmt"
	"time"
)

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

	go func() {
		time.Sleep(2 * time.Second)
		ch1 <- "通道1"
	}()

	go func() {
		time.Sleep(1 * time.Second)
		ch2 <- "通道2"
	}()

	select {
	case msg1 := <-ch1:
		fmt.Println("接收到:", msg1)
	case msg2 := <-ch2:
		fmt.Println("接收到:", msg2)
	}
}

在上述示例中,我们启动了两个 goroutine,分别向 ch1 和 ch2 通道发送数据。然后,使用 select 语句监听这两个通道的接收操作,并执行第一个准备就绪的操作。在本例中,ch2 通道的发送操作先就绪,因此会打印接收到的消息。

定义无限大的通道:

var structChan <-chan struct{} = make(chan struct{})

在 Go 语言中,<-chan struct{} 表示一个无限大小的 channel,用于发送和接收 struct{} 类型的值。struct{} 类型是一个空结构体,它不包含任何字段或方法。

使用 make 函数创建一个无限大小的 channel,并将其赋值给 structChan 变量。这就意味着我们可以将 struct{} 类型的值发送到这个 channel,而不必担心它是否已经满了。因此,structChan 变量现在是一个可以发送和接收 struct{} 类型值的无限大小的 channel。我们可以使用 <-structChan 来接收 channel 发送的值,或者将 struct{} 类型的值发送到 channel 中。

定义超时等待:

package main

import (
	"errors"
	"fmt"
	"time"
)

func Wait(waitTimeout time.Duration, fn Func, checkoutInterval time.Duration) error {
	interrupt := false
	var timeout <-chan time.Time
	if waitTimeout > time.Duration(0) {
		timeout = time.After(waitTimeout)
	} else {
		timeout = time.After(10 * time.Second)
	}

	go func() {
		select {
		case <-timeout:
			interrupt = true
		}
	}()

	wrapError := errors.New("init wrap error")

	for !interrupt {
		ok, err := fn()
		if ok {
			return err
		}
		if err != nil {
			wrapError = fmt.Errorf("[%w] wrap error is: %v", err, wrapError)
		}
		time.Sleep(checkoutInterval)
	}
	return fmt.Errorf("timeout: %w", wrapError)
}

type Func func() (bool, error)

func main() {
	err := Wait(10*time.Second, func() (bool, error) {
		return false, fmt.Errorf("test error")
	}, 100*time.Millisecond)
	fmt.Println(err)
}

这段代码是实现一个名为 Wait 的函数,它可以等待一个函数 fn 执行完成或超时,并返回一个错误。

首先,Wait 函数接受三个参数:waitTimeout 表示等待时间,fn 表示要等待的函数,checkoutInterval 表示每次检查函数执行状态的时间间隔。

接着,Wait 函数会检查 waitTimeout 参数是否大于 0,如果是,则将 timeout 变量设置为 waitTimeout 后面的时间;否则,将 timeout 变量设置为默认的 10 秒。

然后,Wait 函数会启动一个 goroutine,该 goroutine 会监视 timeout 变量,并在超时后将 interrupt 变量设置为 true。

接着,Wait 函数会进入一个循环,每次循环都会调用函数fn,并检查函数执行是否完成或出错。如果函数执行完成,则返回错误;如果函数执行出错,则将错误与上一次的错误进行封装,并继续等待。在每次循环中,Wait 函数都会等待 checkoutInterval 后的时间,以便让函数 fn 有时间进行恢复和重试。

最后,当 interrupt 变量设置为 true 时,Wait 函数会返回一个包含所有错误信息的错误。文章来源地址https://www.toymoban.com/news/detail-699267.html

到了这里,关于Golang开发--channel的使用的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • golang学习-channel管道

    1、定义 管道是golang语言提供的goroutine间的通讯方式,channel可以让一个goroutine发送特定的值给另一个goroutine的通讯机制。 管道是引用类型。 golang语言中channel是一种特殊的类型。像一个队列一样,先进先出。 var 变量 chan 元素类型 var ch1 chan int //声明一个传递整型的管道 var

    2024年01月19日
    浏览(48)
  • golang channel

    channel是不同协程之间异步通信的数据结构。 1 构造 2 读操作 3 写 4 关闭 5 多路复用 实现对多个channel同时监听 三个核心: 1 并发读写安全,需要锁 2 环形缓冲区(数组+头尾指针),好处是复用数组空间,同时保证内存地址连续 3 承载阻塞goroutine的队列 hchan:channel数据结构

    2024年02月11日
    浏览(39)
  • Golang 并发 Channel的用法

    上面是创建了无缓冲的 channel,一旦有 goroutine 往 channel 发送数据,那么当前的 goroutine 会被阻塞住,直到有其他的 goroutine 消费了 channel 里的数据,才能继续运行。 上面示例中的第二个参数表示 channel 可缓冲数据的容量。只要当前 channel 里的元素总数不大于这个可缓冲容量,

    2024年02月21日
    浏览(49)
  • Golang之Channel详细介绍

    一、概述 通道(Channel)是 Golang 在语言级别上提供的 goroutine 间的通讯方式,可以使用channel在多个 goroutine 之间传递消息。如果说 goroutine 是 Go 程序并发的执行体,channel 就是它们之间的连接。channel 是可以让一个 goroutine 发送特定值到另一个 goroutine 的通信机制。 Golang 的并发

    2024年02月12日
    浏览(35)
  • 【Golang】go编程语言适合哪些项目开发?

    前言 在当今数字化时代,软件开发已成为各行各业的核心需求之一。 而选择适合的编程语言对于项目的成功开发至关重要。 本文将重点探讨Go编程语言适合哪些项目开发,以帮助读者在选择合适的编程语言时做出明智的决策。 Go 编程语言适合哪些项目开发? Go是由Google开发

    2024年02月04日
    浏览(76)
  • GO开篇:手握Java走进Golang的世界

    Go(又称 Golang)是 Google 的 Robert Griesemer,Rob Pike 及 Ken Thompson 开发的一种计算机高级编程语言。 Go的官方文档: https://golang.org Go的中文api文档: https://studygolang.com/pkgdoc Go中文社区网站: https://studygolang.com/ 在go退推出之前,已经存在很多高级编程语言了,比如:C、C++、C#、

    2024年02月06日
    浏览(35)
  • golang channel执行原理与代码分析

    使用的go版本为 go1.21.2 首先我们写一个简单的chan调度代码 因为ch的数据获取方式有两种,所以这个示例代码写了两次的ch读与写 老样子通过go build -gcflags -S main.go获取到对应的汇编代码 调度make最终被转换为CALL runtime.makechan 调度ch - struct{}{}最终被转换为CALL runtime.chansend1 由于我

    2024年02月05日
    浏览(34)
  • 001 Golang-channel-practice

    最近在练习并发编程。加上最近也在用Golang写代码,所以记录一下练习的题目。   第一道题目是用10个协程打印100条信息,创建10个协程。每个协程都会有自己的编号。每个协程都会被打印10次。   本题的关键是: 创建goroutines :通过 for i := 0; i 10; i++ 循环,创建了10个gorout

    2024年02月02日
    浏览(44)
  • 详解如何在Golang中监听多个channel

    这篇文章主要为大家详细介绍了如何在Golang中实现监听多个channel,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下    我们可以使用select来同时监听多个goroutine。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package main import (   

    2024年02月13日
    浏览(85)
  • 【Golang中的Go Module使用】

    Golang中的Go Module是一个用于包管理和版本控制的工具。在本文中,我们将深入探讨Go Module的相关知识,包括其定义、使用方法以及一些常见的应用场景。 Go Module是Golang中的包管理和版本控制工具,它的发展历程、用法、意义以及相关指令都对于Golang开发者来说非常重要。在本

    2024年02月16日
    浏览(46)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包