Maison >développement back-end >Golang >Une simple simulation Go - problèmes de concurrence

Une simple simulation Go - problèmes de concurrence

PHPz
PHPzavant
2024-02-09 14:10:10706parcourir

Une simple simulation Go - problèmes de concurrence

L'éditeur php Xinyi vous propose un jeu de simulation Go simple mais intéressant appelé "Concurrency Problem". Ce jeu prend pour thème la programmation simultanée, permettant aux joueurs de découvrir le charme de la programmation simultanée dans un monde virtuel. Dans le jeu, les joueurs doivent écrire du code pour gérer l'exécution simultanée de plusieurs tâches et tester leurs capacités de programmation simultanée. L'interface de jeu est concise et claire, et le fonctionnement est simple, adapté aux débutants pour débuter, et il offre également plusieurs modes de difficulté et de défi parmi lesquels les joueurs peuvent choisir. Que vous soyez débutant ou développeur expérimenté, vous pourrez profiter du plaisir de la programmation simultanée dans ce jeu de simulation.

Contenu de la question

Je suis un étudiant polonais et ce semestre j'ai commencé un cours de programmation concurrente (Go, Ada et quelques langages théoriques et CSP à l'avenir). Pour être honnête, Golang a l'air intéressant mais je suis un peu confus. L'essentiel est que, d'après mon expérience, je me qualifierais de programmeur inférieur à la moyenne. Fondamentalement, ma tâche est de créer une simulation, que je décrirai ainsi :

  • Il existe une grille n*m

  • Les voyageurs peuvent être générés aléatoirement, jusqu'à k voyageurs, chaque voyageur a un identifiant unique (1, 2, 3, etc., jusqu'à k)

  • A des moments aléatoires, si l'espace est libre (je suis sûr que l'espace libre est 0), le voyageur peut se déplacer vers le haut, la gauche, la droite ou le bas sur la grille

  • Dispose également d'une caméra qui imprime parfois l'état actuel du maillage ainsi que les mouvements récents (pas encore implémenté)

  • Officieusement, j'ai entendu dire que je devrais utiliser les chaînes, quoi que cela signifie

Mon idée est de créer une structure avec l'identifiant et les coordonnées de chaque voyageur et d'envoyer son identifiant au canal qui représente la volonté de bouger, puis je choisirai au hasard la direction du mouvement.

Je suis un peu confus au sujet de la concurrence - non seulement si et où dois-je utiliser wgs et mutex, mais aussi par exemple si j'utilise func(){} la boucle doit-elle être à l'intérieur ou à l'extérieur. Je serais très heureux d'apprécier tout conseil, aide ou correctif/idée pour corriger mon code car actuellement, comme vous l'avez deviné, il ne fonctionne pas correctement (par exemple lorsque l'appareil photo imprime la grille, il y a parfois plus de k voyageurs, où plusieurs les voyageurs partagent le même nombre et parfois ils semblent disparaître). J'espère que tout le monde passe une bonne journée et j'apprécierais vraiment toute aide :)

package main;

import(
    "fmt"
    "os"
    "strconv"
    "math/rand"
    //"sync"
    "time"
)

type traveler struct{
    id int;
    x int;
    y int;
}

func main(){

    //command line
    n, err := strconv.Atoi(os.Args[1]);
    m, err := strconv.Atoi(os.Args[2]);
    k, err := strconv.Atoi(os.Args[3]);
    if err != nil{
        panic(err)
        return
    }

    //board
    var grid [][]int;
    grid = make([][]int, n)
    for i:=0; i<n; i++{
        grid[i] = make([]int, m)
    }

    //array of travelers, channel for moves and id
    travelers := make([]traveler, k)
    no_of_travelers := 0;
    move := make(chan int, k);
    id := 1;

    //var wg sync.WaitGroup

    go camera(grid);

    go func() {
        
        for i:=0; i<len(travelers); i++ {   
            if no_of_travelers<k{
                travelers[i] = spawnTraveler(&id,grid);
                no_of_travelers++;
            }
        }
    }()

    go func() {
        for{
            a:= rand.Intn(k);
            sendMoveTraveler(&travelers[a], move);
        }
    }()

    receiveMoveTraveler(travelers, move, grid);

}

func spawnTraveler(id *int, grid [][]int) traveler{
    x:=-1;
    y:=-1;
    for{
        x = rand.Intn(len(grid));
        y = rand.Intn(len(grid));
        if(grid[x][y]==0){
            break;
        }
    }
    t := traveler{id: *id, x: x, y:y};
    grid[x][y] = *id;
    *id++;
    return t;
}


func sendMoveTraveler(t *traveler, move chan int){
        move <- t.id
}

func receiveMoveTraveler(travelers []traveler, move chan int, grid [][]int){
    for{
        id := <- move
        for i:=0; i<len(travelers); i++{
            if travelers[i].id == id{
                direction := rand.Intn(4); //1-left 2-up 3-right 4-down
                switch direction {
                case 0:
                    if travelers[i].x>0 && grid[travelers[i].x-1][travelers[i].y] == 0{
                        grid[travelers[i].x-1][travelers[i].y] = grid[travelers[i].x][travelers[i].y];
                        grid[travelers[i].x][travelers[i].y] = 0;
                        travelers[i].x = travelers[i].x-1;
                        travelers[i].y = travelers[i].y;
                    }
                case 1:
                    if travelers[i].y>0 && grid[travelers[i].x][travelers[i].y-1] == 0{
                        grid[travelers[i].x][travelers[i].y-1] = grid[travelers[i].x][travelers[i].y];
                        grid[travelers[i].x][travelers[i].y] = 0;
                        travelers[i].x = travelers[i].x;
                        travelers[i].y = travelers[i].y-1;
                    }
                case 2:
                    if travelers[i].x<len(grid)-1 && grid[travelers[i].x+1][travelers[i].y] == 0{
                        grid[travelers[i].x+1][travelers[i].y] = grid[travelers[i].x][travelers[i].y];
                        grid[travelers[i].x][travelers[i].y] = 0;
                        travelers[i].x = travelers[i].x+1;
                        travelers[i].y = travelers[i].y;
                    }
                case 3:
                    if travelers[i].y<len(grid)-1 && grid[travelers[i].x][travelers[i].y+1] == 0{
                        grid[travelers[i].x][travelers[i].y+1] = grid[travelers[i].x][travelers[i].y];
                        grid[travelers[i].x][travelers[i].y] = 0;
                        travelers[i].x = travelers[i].x;
                        travelers[i].y = travelers[i].y+1;
                    }
                }
                //fmt.Println("Ściagnalem ruch", travelers[i].id);
            }
        }
    }
}

func camera(grid [][]int){
    for{
    for i:=0; i<len(grid); i++{
        for j:=0; j<len(grid); j++{
            if grid[i][j]!= 0{
                fmt.Printf("%02d ", grid[i][j]);
            } else{
                fmt.Printf("-- ");
            }
        }
        fmt.Println();
    }
    fmt.Println();
    time.Sleep(time.Second * 3);
}
}

Je suis un peu dépassé par toutes les idées - wgs, mutex, atomes, etc.

Solution

  • Si vous souhaitez traiter le travail simultanément (par exemple, prendre un instantané de l'appareil photo et déplacer le voyageur peuvent se produire en même temps), les goroutines sont des threads légers.
  • Les canaux sont utilisés pour transférer des données entre les routines Go.
  • Mutex est utilisé pour permettre aux goroutines d'ajouter des verrous sur les données partagées pour un accès exclusif aux données afin d'éviter les conditions de concurrence.

Cela étant dit :

  • Exécuter des instantanés de caméra dans une goroutine tout en laissant le voyageur se déplacer dans une autre goroutine semble bien. Générer la Goroutine n'est pas nécessaire, vous ne devez le faire qu'une seule fois, vous pouvez donc l'exécuter dans la Goroutine principale.
  • Dans votre cas, les chaînes n'apportent aucun avantage. Vous avez un Goroutine qui génère un message et l'envoie via un canal à un autre Goroutine qui effectuera le mouvement. Vous pouvez faire tout cela séquentiellement dans une seule goroutine et éviter toute complexité inutile. Les canaux sont utiles pour différents cas d’utilisation, mais ici ils sont redondants.
  • Puisque vous avez deux goroutines accédant à la mémoire partagée (la grille), vous avez besoin d'un mutex pour éviter les conditions de concurrence. Chaque fois que l'un de ces éléments s'exécute, il doit se "verrouiller", terminer son travail, puis se "déverrouiller". Un autre goroutine se bloquera à l'étape de verrouillage jusqu'à ce que le premier goroutine qui a acquis le verrou se déverrouille. Vous pouvez l'optimiser davantage à l'aide de verrous en lecture/écriture (les verrous en lecture ne sont nécessaires que pour la caméra et les verrous en lecture/écriture pour la coroutine mobile)
  • Si vous souhaitez plus d'aléatoire, vous pouvez créer une goroutine pour chaque voyageur.

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:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer