golang特性3

golang特性

通过通信共享内存

在 Go 中,通信共享内存是通过通道来实现的。Go 语言的设计哲学之一就是“不要通过共享内存来通信,而应通过通信来共享内存”。这意味着不鼓励直接在多个协程之间共享内存,而是通过通道进行数据交换,以确保并发安全性。

应用场景

  • 协程间通信,即协程间数据传递
  • 并发场景下利用channel的阻塞机制,作为同步机制(类似队列)
  • 利用channel关闭时发送广播的特性,作为协程退出通知

任务分发与结果收集: 可以使用一个或多个协程生成任务,并将这些任务发送到一个通道中,然后另一个或多个协程从该通道接收任务并执行。执行结果可以发送到另一个通道中,由另一个协程负责收集和处理
生产者-消费者模型: 可以使用通道来实现生产者-消费者模型,其中一个或多个协程负责生成数据并将其发送到通道中(生产者),另一个或多个协程从通道中接收数据并进行处理(消费者)。
协程池: 可以使用通道来实现协程池,其中一个协程负责接收任务并将其发送到通道中,另一个或多个协程从通道中接收任务并执行。
同步任务并行执行: 可以使用通道来控制多个协程的执行顺序,通过在通道中发送信号来同步它们的执行
资源访问控制可以使用通道来控制对共享资源的访问,例如使用一个带有缓冲区的通道作为互斥锁,只有当通道中有空间时才允许写入,否则阻塞。
协程退出通知: 使用通道的关闭机制可以作为一种通知机制,当一个协程需要通知其他协程退出时,可以关闭一个通道,其他协程通过检测通道关闭来得知退出的信号,从而安全地结束执行。
使用通道实现并发安全的共享内存的基本示例:

package main

import (
	"fmt"
	"sync"
)

func main() {
	var wg sync.WaitGroup
	ch := make(chan int)

	wg.Add(2)

	// 协程1:向通道发送数据
	go func() {
		defer wg.Done()
		for i := 0; i < 5; i++ {
			ch <- i // 将数据发送到通道
		}
		close(ch) // 关闭通道
	}()

	// 协程2:从通道接收数据
	go func() {
		defer wg.Done()
		for num := range ch { // 从通道接收数据,直到通道被关闭
			fmt.Println("Received:", num)
		}
	}()

	wg.Wait()
}

创建了一个通道 ch,用于在两个协程之间传递数据。协程1负责向通道发送数据,协程2负责从通道接收数据。通道的阻塞特性确保了在数据发送和接收之间的同步,而通道的关闭机制则用于结束数据传递的过程。

channel 通过通讯共享内存

通道本质上是一种类型安全的消息队列,用于在协程之间传递数据。通过使用通道,可以避免并发环境下的竞态条件和锁问题,提高代码的可读性和可维护性。

通道的使用方式很简单:可以通过内置函数make()来创建一个通道,然后通过<-运算符来发送和接收数据。通道可以是有缓冲的,也可以是无缓冲的,具体选择取决于应用场景。

  1. channel的方向,读、写、读写
  2. channel 协程间通信信道
  3. channel 阻塞协程
  4. channel 并发场景下的同步机制
  5. channel 通知协程退出
  6. channel 的多路复用
package main

import (
	"fmt"
	"time"
)

func sender(ch chan<- int) {
	for i := 0; i < 5; i++ {
		ch <- i // 将数据发送到通道
		time.Sleep(time.Second)
	}
	close(ch) // 关闭通道
}

func receiver(ch <-chan int) {
	for {
		val, ok := <-ch // 从通道接收数据
		if !ok {
			fmt.Println("通道已关闭")
			return
		}
		fmt.Println("接收到数据:", val)
	}
}

func main() {
	ch := make(chan int) // 创建一个整型通道
	go sender(ch)
	go receiver(ch)
	time.Sleep(6 * time.Second) // 等待一段时间确保程序执行完毕
}

sender函数负责往通道发送数据,而receiver函数负责从通道接收数据。主函数创建了一个通道,并启动了两个协程来执行发送和接收操作。

通过通道,这两个协程之间就实现了数据的传递,而不需要显式地共享内存。这种通信方式更加安全和可靠,避免了并发编程中常见的竞态条件和死锁问题。
安全可靠的原因
这种通过通道进行通信的方式更加安全和可靠,主要是因为它遵循了以下几个原则:
基于消息传递的通信模型:通道是基于消息传递的通信模型,它强调的是发送方和接收方之间的解耦。发送方通过通道将数据发送给接收方,而不需要关心接收方何时会收到数据,接收方也无需关心数据是何时发送的。这种解耦的特性减少了代码之间的耦合度,使得代码更加清晰和易于理解。
内置的同步机制:通道在实现上具有内置的同步机制,发送操作和接收操作都是原子性的当一个协程尝试向通道发送数据时,如果通道已满(对于有缓冲的通道),发送操作会被阻塞,直到有接收方从通道中接收数据为止;同样,如果一个协程尝试从通道接收数据时,如果通道为空,接收操作也会被阻塞,直到有发送方向通道发送数据为止。这种阻塞机制有效地避免了竞态条件的发生。
关闭通道和广播特性通道支持关闭操作,可以通过close()函数关闭通道。当通道关闭后,接收方可以通过判断通道的关闭状态来知道是否还有数据可以接收,这样就避免了接收方因为等待数据而陷入死锁状态。另外,关闭通道时会触发广播机制,所有正在等待接收数据的协程都会被唤醒,从而可以及时退出或进行其他操作
通过通道进行通信的方式更加安全和可靠,因为它提供了简单且有效的同步机制,避免了常见的并发编程问题,如竞态条件和死锁。同时,它也符合 Go 语言的设计理念,即“不要通过共享内存来通信,而是通过通信来共享内存”,使得代码更加清晰、易于理解和维护。

注意

channel 用于协程间通讯,必须存在读写双方,否则将造成死锁

果一个通道没有发送方或接收方,就无法完成通信操作。例如,如果一个协程试图向一个没有接收方的通道发送数据,发送操作将永远被阻塞,导致该协程无法继续执行下去,从而产生死锁。同样地,如果一个协程试图从一个没有发送方的通道接收数据,接收操作也将永远被阻塞,同样会导致死锁。

因此,在使用通道进行协程间通信时,需要确保发送方和接收方的存在,并且它们能够正确地协调完成数据的发送和接收操作,以避免死锁的发生。
在Go中,可以使用内置的len和cap函数来确定通道的发送方和接收方是否存在。

使用len(ch)函数可以检查通道中当前排队的元素数量。如果通道为空(没有发送方),则len(ch)返回0。
使用cap(ch)函数可以检查通道的容量。如果通道是无缓冲的(容量为0),则它必须有一个接收方,否则发送操作将被阻塞。如果通道是有缓冲的,即容量大于0,那么即使没有接收方,发送操作也可能不被阻塞,因为缓冲区中可以存放一定数量的元素。

  • 23
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值