Maison >Problème commun >Comment implémenter le contrôle de concurrence dans le langage Go

Comment implémenter le contrôle de concurrence dans le langage Go

DDD
DDDoriginal
2023-06-08 14:20:471870parcourir

Méthodes d'implémentation du contrôle de concurrence dans le langage go : 1. WaitGroup, le traitement des tâches de plusieurs goroutines a une relation de dépendance ou d'épissage ; 2. Channel, vous pouvez activement annuler le transfert de données dans plusieurs groutines ; remplacer WaitGroup Work pour répondre aux fonctions de Context ; 3. Contexte, propagation du signal entre les groutines multi-niveaux, y compris la propagation des métadonnées, l'annulation de la propagation du signal, le contrôle du délai d'attente, etc.

Comment implémenter le contrôle de concurrence dans le langage Go

L'environnement d'exploitation de cet article : système Windows 10, version go1.20, ordinateur Dell g3.

Dans Golang, vous pouvez ouvrir une goroutine via le mot-clé go, afin de pouvoir facilement écrire du code simultané dans Go. Mais comment contrôler efficacement ces groutines qui s'exécutent simultanément ?

Quand il s'agit de contrôle de concurrence, la première chose à laquelle beaucoup de gens pensent, ce sont les verrous. Golang fournit également des mécanismes liés au verrouillage, notamment le verrouillage mutex sync.Mutex et le verrouillage en lecture-écriture sync.RWMutex. En plus des verrous, il existe également des opérations atomiques sync/atomic, etc. Cependant, ces mécanismes se concentrent sur la sécurité simultanée des données des goroutines. Ce que cet article veut discuter, c'est du contrôle du comportement de concurrence de goroutine.

Dans le contrôle des comportements simultanés des goroutines, il existe trois méthodes courantes, à savoir WaitGroup, Channel et Context.

WaitGroup

WaitGroup se trouve sous le package de synchronisation. Son utilisation est la suivante.

func main() {
  var wg sync.WaitGroup

  wg.Add(2) //添加需要完成的工作量2

  go func() {
    wg.Done() //完成工作量1
    fmt.Println("goroutine 1 完成工作!")
  }()

  go func() {
    wg.Done() //完成工作量1
    fmt.Println("goroutine 2 完成工作!")
  }()

  wg.Wait() //等待工作量2均完成
  fmt.Println("所有的goroutine均已完成工作!")}输出:
//goroutine 2 完成工作!
//goroutine 1 完成工作!
//所有的goroutine均已完成工作!

WaitGroup Cette méthode de contrôle de concurrence est particulièrement adaptée aux situations où une certaine tâche nécessite que plusieurs goroutines travaillent ensemble. Chaque goroutine ne peut effectuer qu'une partie de la tâche. La tâche n'est terminée que lorsque toutes les goroutines le sont. complété. WaitGroup a donc la même signification que son nom, qui est une façon d'attendre.

Cependant, dans les affaires réelles, il existe un scénario : lorsqu'une certaine exigence est remplie, une certaine goroutine doit être activement notifiée pour mettre fin. Par exemple, si nous démarrons une goroutine de surveillance en arrière-plan, lorsque la surveillance n'est plus nécessaire, nous devons notifier la goroutine de surveillance de la fin, sinon elle continuera à tourner au ralenti et provoquera des fuites.

Channel

Pour le scénario ci-dessus, WaitGroup ne peut rien faire. La méthode la plus simple que l'on puisse imaginer est de définir une variable globale et de la notifier en modifiant cette variable ailleurs. La goroutine d'arrière-plan vérifiera constamment cette variable si elle constate que la variable a changé, elle se fermera cependant. est quelque peu encombrant. Dans ce cas, canal+sélection peut s'avérer utile.

func main() {
  exit := make(chan bool)

  go func() {
    for {
      select {
      case <-exit:
        fmt.Println("退出监控")
        return
      default:
        fmt.Println("监控中")
        time.Sleep(2 * time.Second)
      }
    }
  }()

  time.Sleep(5 * time.Second)
  fmt.Println("通知监控退出")
  exit <- true

  //防止main goroutine过早退出
  time.Sleep(5 * time.Second)}输出:
//监控中
//监控中
//监控中
//通知监控退出
//退出监控

Cette combinaison canal+sélection est une manière plus élégante de notifier la goroutine de la fin.

Cependant, cette solution présente également des limites. Imaginez, et s'il y avait plusieurs goroutines qui devaient toutes être contrôlées pour se terminer ? Et si ces goroutines engendraient d’autres goroutines ? Bien sûr, nous pouvons définir de nombreux canaux pour résoudre ce problème, mais la chaîne relationnelle de goroutine conduit à la complexité de ce scénario.

Context

Les scénarios ci-dessus sont courants sous le modèle d'architecture CS. Dans Go, une goroutine distincte (A) est souvent ouverte pour chaque client afin de gérer sa série de requêtes, et souvent un seul A demandera également d'autres services (démarre une autre goroutine B), et B peut également demander une autre goroutine C, C. envoie ensuite la requête au serveur de Databse, par exemple. Imaginez que lorsque le client se déconnecte, A, B et C qui lui sont associés doivent se fermer immédiatement avant que le système puisse récupérer les ressources occupées par A, B et C. Quitter A est simple, mais comment avertir B et C de quitter également ?

À ce moment, le Contexte apparaît.

func A(ctx context.Context, name string)  {
  go B(ctx ,name) //A调用了B  for {
    select {
    case <-ctx.Done():
      fmt.Println(name, "A退出")
      return
    default:
      fmt.Println(name, "A do something")
      time.Sleep(2 * time.Second)
    }
  }}func B(ctx context.Context, name string)  {
  for {
    select {
    case <-ctx.Done():
      fmt.Println(name, "B退出")
      return
    default:
      fmt.Println(name, "B do something")
      time.Sleep(2 * time.Second)
    }
  }}func main() {
  ctx, cancel := context.WithCancel(context.Background())

  go A(ctx, "【请求1】") //模拟client来了1个连接请求

  time.Sleep(3 * time.Second)
  fmt.Println("client断开连接,通知对应处理client请求的A,B退出")
  cancel() //假设满足某条件client断开了连接,那么就传播取消信号,ctx.Done()中得到取消信号

  time.Sleep(3 * time.Second)}输出:
//【请求1】 A do something
//【请求1】 B do something
//【请求1】 A do something
//【请求1】 B do something
//client断开连接,通知对应处理client请求的A,B退出
//【请求1】 B退出
//【请求1】 A退出

Dans l'exemple, une demande de connexion est simulée par le client et Goroutine A est ouverte en conséquence pour le traitement A et B utilisent le contexte pour le suivi. Annuler la notification de la fonction Une fois annulées, les deux goroutines seront terminées.

C'est la capacité de contrôle de Context. C'est comme un contrôleur. Après avoir appuyé sur le commutateur, tous les sous-contextes basés sur ce contexte ou dérivés de celui-ci recevront des notifications, puis des opérations de nettoyage pourront être effectuées. , et publie enfin le goroutine, qui résout avec élégance le problème du goroutine incontrôlable après le démarrage.

L'utilisation détaillée de Context dépasse la portée de cet article. Il y aura un article de suivi expliquant spécifiquement le package Context, alors restez à l’écoute.

Summary

Cet article répertorie trois modes de contrôle du comportement de concurrence dans Golang. Il n’y a pas de bonne ou de mauvaise distinction entre les modes, cela dépend simplement de l’utilisation de solutions appropriées aux différents scénarios. Dans les projets réels, plusieurs méthodes sont souvent utilisées en combinaison.

  • WaitGroup : Le traitement des tâches de plusieurs goroutines a des dépendances ou des relations d'épissage.
  • channel+select : Vous pouvez annuler activement la goroutine ; le transfert de données dans plusieurs groutines ; le canal peut remplacer le travail de WaitGroup, mais cela augmentera la complexité de la logique du code ; plusieurs canaux peuvent répondre aux fonctions de Context ; , et de même, cela rendra la logique du code complexe.
  • Contexte : Propagation du signal entre groutines multi-niveaux (y compris propagation des métadonnées, annulation de la propagation du signal, contrôle du délai d'attente, etc.).

Dans Golang, vous pouvez ouvrir une goroutine via le mot-clé go, afin de pouvoir facilement écrire du code simultané dans Go. Mais comment contrôler efficacement ces groutines qui s'exécutent simultanément ?

Quand il s'agit de contrôle de concurrence, la première chose à laquelle beaucoup de gens pensent, ce sont les verrous. Golang fournit également des mécanismes liés au verrouillage, notamment le verrouillage mutex sync.Mutex et le verrouillage en lecture-écriture sync.RWMutex. En plus des verrous, il existe également des opérations atomiques sync/atomic, etc. Cependant, ces mécanismes se concentrent sur la sécurité simultanée des données des goroutines. Ce que cet article veut discuter, c'est du contrôle du comportement de concurrence de goroutine.

Dans le contrôle du comportement simultané des goroutines, il existe trois méthodes courantes, à savoir WaitGroup, Channel et Context.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn