设为首页 加入收藏

TOP

go channel原理及使用场景(五)
2023-07-23 13:29:02 】 浏览:92
Tags:channel
eleasetime = cputicks() } gp := sg.g gp.param = unsafe.Pointer(sg) sg.success = false if raceenabled { raceacquireg(gp, c.raceaddr()) } glist.push(gp) } unlock(&c.lock) // 为所有被阻塞的 Goroutine 调用 runtime.goready 触发调度。 for !glist.empty() { gp := glist.pop() gp.schedlink = 0 goready(gp, 3) }

使用场景

报错情形

  • 往一个关闭的channel发送数据会报错:panic: send on closed channel
  • 关闭一个nil的chan会报错:panic: close of nil channel
  • 关闭一个已经关闭的channel报错:panic: close of closed channel

1、一个经典的算法题

有4个goroutine,编号为1、2、3、4。每秒钟会有一个goroutine打印出自己的编号,要求写一个程序,让输出的编号总是按照1、2、3、4、1、2、3、4...的顺序打印出来

package main

import (
	"fmt"
	"time"
)

func main() {
  // 4个channel
	chs := make([]chan int, 4)
	for i, _ := range chs {
		chs[i] = make(chan int)
    // 开4个协程
		go func(i int) {
			for {
        // 获取当前channel值并打印
				v := <-chs[i]
				fmt.Println(v + 1)
				time.Sleep(time.Second)
        // 把下一个值写入下一个channel,等待下一次消费
				chs[(i+1)%4] <- (v + 1) % 4
			}

		}(i)
	}

  // 往第一个塞入0
	chs[0] <- 0
	select {}
}

2、限流器

package main

import (
	"fmt"
	"time"
)

func main() {
	// 每次处理3个请求
	chLimit := make(chan struct{}, 3)
	for i := 0; i < 20; i++ {
		chLimit <- struct{}{}
		go func(i int) {
			fmt.Println("下游服务处理逻辑...", i)
			time.Sleep(time.Second * 3)
			<-chLimit
		}(i)
	}
	time.Sleep(30 * time.Second)
}

如果觉得sleep太丑太暴力,可以用waitGroup控制结束时机

package main

import (
	"fmt"
	"sync"
	"time"
)

var wg sync.WaitGroup

func main() {
	// 每次处理3个请求
	chLimit := make(chan struct{}, 3)
	for i := 0; i < 20; i++ {
		chLimit <- struct{}{}
		wg.Add(1)
		go func(i int) {
			fmt.Println("下游服务处理逻辑...", i)
			time.Sleep(time.Second * 3)
			<-chLimit
			wg.Done()
		}(i)
	}
	wg.Wait()
}

3、优雅退出

package main

import (
	"fmt"
	"log"
	"os"
	"os/signal"
	"syscall"
	"time"
)

func main() {
	var closing = make(chan struct{})
	var closed = make(chan struct{})

	go func() {
		for {
			select {
			case <-closing:
				return
			default:
				fmt.Println("业务逻辑...")
				time.Sleep(1 * time.Second)
			}
		}
	}()

	termChan := make(chan os.Signal)
  // 监听退出信号
	signal.Notify(termChan, syscall.SIGINT, syscall.SIGTERM)
	<-termChan

  // 退出中
	close(closing)

	// 退出之前清理一下
	go doCleanup(closed)

	select {
	case <-closed:
	case <-time.After(time.Second):
		log.Println("清理超时不等了")
	}

	log.Println("优雅退出")
}

func doCleanup(closed chan struct{}) {
	time.Sleep(time.Minute)
  // 清理完后退出
	close(closed)
}

4、实现互斥锁

初始化一个缓冲区为1的channel,放入元素代表一把锁,谁获取到这个元素就代表获取了这把锁,释放锁的时候再把这个元素放回channel

package main

import (
	"log"
	"time"
)

type Mutex struct {
	ch chan struct{}
}

// 初始化锁
func NewMutex() *Mutex {
	mu := &Mutex{make(chan struct{}, 1)}
	mu.ch <- struct{}{}
	return mu
}

// 加锁,阻塞获取
func (m *Mutex) Lock()  {
	<- m.ch
}

// 释放锁
func (m *Mutex) Unlock()  {
	select {
    // 成功写入channel代表释放成功
	case m.ch <- struct{}{}:
	default:
		panic("unlock of unlocked mutex")
	}
}

// 尝试获取锁
func (m *Mutex) TryLock() bool {
	select {
	case <-m.ch:
		return true
	default:

	}
	return false
}

func (m *Mutex) LockTimeout(timeout time.Durati
首页 上一页 2 3 4 5 下一页 尾页 5/5/5
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇分享Go书籍-《Go Web编程》 下一篇Go设计模式学习准备——下载bilib..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目