Scénarios d'utilisation et précautions du canal Go





1 Notification de signal

Il existe souvent de tels scénarios, lorsque la collecte d'informations est terminée, l'aval est notifié de commencer à calculer les données:

import (
	"log"
	"time"
)

func main() {
    
    
	isOver := make(chan struct{
    
    })
	go func() {
    
    
		collectMsg(isOver)
	}()
	<-isOver
	calculateMsg()
}

// 采集
func collectMsg(isOver chan struct{
    
    }) {
    
    
	log.Println("开始采集")
	time.Sleep(3000 * time.Millisecond)
	log.Println("完成采集")
	isOver <- struct{
    
    }{
    
    }
}

// 计算
func calculateMsg() {
    
    
	log.Println("开始进行数据分析")
}

Sortie du programme:

2021/01/25 09:48:23 开始采集
2021/01/25 09:48:26 完成采集
2021/01/25 09:48:26 开始进行数据分析

Si vous n'utilisez que des opérations de notification, le type utilise struct {}. Parce que les structures vides ne prennent pas de place dans la mémoire, ne me croyez pas.

func main() {
    
    
	res := struct{
    
    }{
    
    }
	fmt.Println("占用空间:", unsafe.Sizeof(res))
}

Sortie du programme:

占用空间: 0




2 Délai d'exécution de la tâche

Lors du traitement des tâches, le temps de traitement de la tâche ne peut pas être garanti et certains contrôles de délai d'expiration sont généralement ajoutés pour gérer les exceptions.

func main() {
    
    
	select {
    
    
	case <-doWork():
		log.Println("任务在规定时间内结束")
	case <-time.After(1 * time.Second):
		log.Println("任务处理超时")
	}
}

func doWork() <-chan struct{
    
    } {
    
    
	ch := make(chan struct{
    
    })
	go func() {
    
    
		// 处理耗时任务
		log.Println("开始处理任务")
		time.Sleep(2 * time.Second)
		ch <- struct{
    
    }{
    
    }
	}()
	return ch
}

Sortie du programme:

2021/01/25 09:58:10 开始处理任务
2021/01/25 09:58:11 任务处理超时




3 Modèle de production et de consommation

Le producteur n'a qu'à prêter attention à la production, pas au comportement de consommation du consommateur, encore moins à savoir si le consommateur a terminé l'exécution. Les consommateurs ne se soucient que des tâches de consommation, pas de la façon de produire.

func main() {
    
    
	ch := make(chan int, 10)
	go consumer(ch)
	go producer(ch)
	time.Sleep(3 * time.Second)
}

// 一个生产者
func producer(ch chan int) {
    
    
	for i := 0; i < 10; i++ {
    
    
		ch <- i
	}
	close(ch) // 关闭通道
}

// 消费者
func consumer(ch chan int) {
    
    
	for i := 0; i < 5; i++ {
    
    
		// 5个消费者
		go func(id int) {
    
    
			for {
    
    
				item, ok := <-ch
				// 如果等于false 说明通道已关闭
				if !ok {
    
    
					return
				}
				fmt.Printf("消费者:%d,消费了:%d\n", id, item)

				time.Sleep(50 * time.Millisecond) // 给别人一点机会不会吃亏
			}
		}(i)
	}
}

Sortie du programme:

消费者:4,消费了:0
消费者:0,消费了:1
消费者:1,消费了:2
消费者:2,消费了:3
消费者:3,消费了:4
消费者:3,消费了:6
消费者:1,消费了:8
消费者:2,消费了:7
消费者:0,消费了:5
消费者:4,消费了:9




4 Transfert de données

Pour une question intéressante pour les geeks, supposons qu'il y ait 4 goroutines, numérotées 1, 2, 3, 4. Toutes les 3 secondes, un goroutine imprime son propre numéro. Maintenant, vous pouvez écrire un programme qui exige que les numéros de sortie soient toujours imprimés dans l'ordre de 1, 2, 3, 4. Similaire à l'image ci-dessous:
Insérez la description de l'image ici

type token struct{
    
    }

func main() {
    
    
	num := 4
	var chs []chan token
	// 4 个 channel
	for i := 0; i < num; i++ {
    
    
		chs = append(chs, make(chan token))
	}
	// 4 个 协程
	for j := 0; j < num; j++ {
    
    
		go worker(j, chs[j], chs[(j+1)%num])
	}
	// 先把令牌交给第一个
	chs[0] <- struct{
    
    }{
    
    }
	select {
    
    }
}

func worker(id int, currentCh chan token, nextCh chan token) {
    
    
	for {
    
    
		// 对应 work 取得令牌
		token := <-currentCh
		fmt.Println(id + 1)
		time.Sleep(3 * time.Second)
		// 传递给下一个
		nextCh <- token
	}
}

Sortie du programme:

1
2
3
4
1
2
3
4
.
.
.




5 Contrôlez le nombre de

J'écris souvent des scripts pour extraire des données en interne ou en externe aux petites heures du matin, mais si les demandes simultanées ne sont pas contrôlées, cela entraînera souvent un débordement de groutine, puis remplira les ressources du processeur. Souvent, des choses qui ne peuvent pas être contrôlées signifient que de mauvaises choses se produiront. Pour nous, le nombre de concurrence peut être contrôlé via des canaux.

func main() {
    
    
	limit := make(chan struct{
    
    }, 10)
	jobCount := 100
	for i := 0; i < jobCount; i++ {
    
    
		go func(index int) {
    
    
			limit <- struct{
    
    }{
    
    }
			job(index)
			<-limit
		}(i)
	}

	time.Sleep(30 * time.Second)
}

func job(index int) {
    
    
	// 耗时任务
	time.Sleep(2 * time.Second)
	fmt.Printf("任务:%d 已完成\n", index)
}

Le code ci-dessus contrôle que seuls 10 et coroutines sont exécutés à tout moment

Bien sûr, sync.waitGroup peut également contrôler le nombre de coroutines simultanées:

func main() {
    
    
	var wg sync.WaitGroup
	jobCount := 100
	limit := 10
	for i := 0; i <= jobCount; i += limit {
    
    
		index := 0
		if i-limit >= 0 {
    
    
			index = i - limit
		}
		for j := index; j < i; j++ {
    
    
			wg.Add(1)
			go func(item int) {
    
    
				defer wg.Done()
				job(item)
			}(j)
		}
		wg.Wait()
		fmt.Println("------------")
	}
}

func job(index int) {
    
    
	// 耗时任务
	time.Sleep(1 * time.Second)
	fmt.Printf("任务:%d 已完成\n", index)
}




6 mutex personnalisés

Un petit verrou mutex peut être implémenté via channel. En définissant un canal avec un tampon de 1, si les données sont envoyées avec succès au canal, cela signifie que le verrou est obtenu, sinon le verrou est pris par quelqu'un d'autre, en attendant que quelqu'un le déverrouille.

type ticket struct{
    
    }

type Mutex struct {
    
    
	ch chan ticket
}

// 创建一个缓冲区为1的通道作
func newMutex() *Mutex {
    
    
	return &Mutex{
    
    ch: make(chan ticket, 1)}
}

// 谁能往缓冲区为1的通道放入数据,谁就获取了锁
func (m *Mutex) Lock() {
    
    
	m.ch <- struct{
    
    }{
    
    }
}

// 解锁就把数据取出
func (m *Mutex) unLock() {
    
    
	select {
    
    
	case <-m.ch:
	default:
		panic("已经解锁了")
	}
}

func main() {
    
    
	mutex := newMutex()
	go func() {
    
    
		// 如果是1先拿到锁,那么2就要等1秒才能拿到锁
		mutex.Lock()
		fmt.Println("任务1拿到锁了")
		time.Sleep(1 * time.Second)
		mutex.unLock()
	}()
	go func() {
    
    
		mutex.Lock()
		// 如果是2拿先到锁,那么1就要等2秒才能拿到锁
		fmt.Println("任务2拿到锁了")
		time.Sleep(2 * time.Second)
		mutex.unLock()
	}()
	time.Sleep(500 * time.Millisecond)
	// 用了一点小手段这里最后才能拿到锁
	mutex.Lock()
	mutex.unLock()
	close(mutex.ch)
}




Faites attention à quelles opérations du canal provoqueront la panique?


1 La fermeture d'un canal avec une valeur nulle provoquera la panique

package main

func main() {
    
    
  var ch chan struct{
    
    }
  close(ch)
}

Insérez la description de l'image ici

2 La fermeture d'un canal fermé provoquera la panique

package main

func main() {
    
    
	ch := make(chan struct{
    
    })
	close(ch)
	close(ch)
}

Insérez la description de l'image ici

3 Envoyer des données vers un canal fermé

package main

func main() {
    
    
	ch := make(chan struct{
    
    })
	close(ch)
	ch <- struct{
    
    }{
    
    }
}

Insérez la description de l'image ici

Je suppose que tu aimes

Origine blog.csdn.net/QiuHaoqian/article/details/113103478
conseillé
Classement