Maison >développement back-end >Golang >Apprenez à concevoir une programmation simultanée évolutive Select Channels Go dans Golang
Apprenez à concevoir une programmation simultanée Select Channels Go évolutive dans Golang
Introduction :
Le langage Go est un langage de programmation simultané efficace et concis, et son modèle de concurrence est principalement basé sur goroutine et canal. Grâce au mécanisme de communication intuitif des threads et canaux légers des goroutines, le modèle de programmation simultanée du langage Go offre un moyen efficace de gérer les tâches simultanées.
Dans la langue Go, il est courant d'utiliser des canaux de communication. En plus de l'utilisation de base des canaux, nous pouvons également utiliser l'instruction select pour gérer la sélection et la communication de plusieurs canaux afin d'obtenir une programmation simultanée plus flexible et évolutive.
Cet article prendra un cas comme exemple pour présenter comment utiliser des instructions et des canaux sélectionnés pour concevoir un programme concurrent évolutif.
Cas :
Nous supposons qu'il existe un distributeur de tâches et que plusieurs threads de travail obtiennent des tâches du distributeur de tâches pour les traiter. Le répartiteur de tâches ajuste dynamiquement la stratégie d'allocation des tâches en fonction de la longueur de la file d'attente des tâches et du nombre de threads de travail.
Tout d'abord, nous définissons une structure de tâches Task :
type Task struct { ID int Value int }
Ensuite, nous créons un répartiteur de tâches Dispatcher et implémentons les méthodes associées :
type Dispatcher struct { workerCount int taskQueue chan Task workerDone chan struct{} workerFinish chan struct{} } func NewDispatcher(workerCount int) *Dispatcher { return &Dispatcher{ workerCount: workerCount, taskQueue: make(chan Task), workerDone: make(chan struct{}, workerCount), workerFinish: make(chan struct{}), } } func (d *Dispatcher) Start() { for i := 0; i < d.workerCount; i++ { go d.worker() } go d.adjust() } func (d *Dispatcher) worker() { for task := range d.taskQueue { // 处理任务 fmt.Printf("Worker[%d] processing task %d ", task.ID, task.Value) time.Sleep(1 * time.Second) d.workerDone <- struct{}{} } } func (d *Dispatcher) adjust() { for { select { case <-d.workerFinish: d.workerCount-- if d.workerCount == 0 { return } case <-time.After(5 * time.Second): if len(d.taskQueue) > 10 && d.workerCount < 5 { d.workerCount++ go d.worker() } } } } func (d *Dispatcher) Dispatch(task Task) { d.taskQueue <- task } func (d *Dispatcher) Wait() { for i := 0; i < d.workerCount; i++ { <-d.workerDone } close(d.taskQueue) close(d.workerFinish) close(d.workerDone) }
Dans Dispatcher, nous définissons 4 canaux : taskQueue pour la réception et la distribution des tâches, workerDone est utilisé pour le retour des tâches. signaux d'achèvement de tâche, et workerFinish est utilisé pour compter et ajuster les threads de travail.
La méthode Start est utilisée pour démarrer le thread de travail et le thread d'ajustement des tâches, où la méthode de travail est l'implémentation spécifique du thread de travail. Chaque thread de travail retire la tâche de taskQueue pour le traitement et envoie le signal d'achèvement de la tâche à workerDone. La méthode
adjust est l'implémentation spécifique du fil d'ajustement des tâches. Il utilise select pour surveiller deux canaux. Lorsque workerFinish reçoit le signal, cela signifie qu'un thread de travail a terminé la tâche et que des ajustements de personnel doivent être effectués. Lorsque le minuteur time.After se déclenche, cela signifie que la longueur de la file d'attente des tâches est trop longue et que des threads de travail doivent être ajoutés pour gérer plus de tâches. En ajustant dynamiquement le nombre de threads de travail, nous pouvons utiliser pleinement les ressources du système et maintenir les tâches traitées rapidement.
La méthode Dispatch est utilisée pour soumettre des tâches au répartiteur de tâches. La méthode Wait est utilisée pour attendre la fin de toutes les tâches.
Exemple d'utilisation :
func main() { dispatcher := NewDispatcher(3) dispatcher.Start() for i := 0; i < 20; i++ { task := Task{ ID: i, Value: i, } dispatcher.Dispatch(task) } dispatcher.Wait() }
Dans cet exemple, nous créons un Dispatcher et démarrons 3 threads de travail. Ensuite, nous avons distribué 20 tâches au Dispatcher. Enfin, attendez la fin de toutes les tâches via la méthode Wait.
Résumé :
En utilisant des instructions et des canaux sélectionnés, nous pouvons concevoir de manière flexible des programmes simultanés évolutifs. Dans ce cas, nous montrons comment utiliser select et canal pour implémenter un répartiteur de tâches qui ajuste dynamiquement la stratégie de répartition des tâches. En utilisant cette méthode, nous pouvons utiliser pleinement les ressources du système et maintenir les tâches traitées rapidement.
En programmation simultanée réelle, nous pouvons étendre et optimiser davantage ce modèle en fonction de besoins et de scénarios spécifiques. J'espère que cet article pourra aider les lecteurs à mieux comprendre et utiliser la sélection et les canaux pour concevoir des programmes simultanés Go évolutifs.
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!