首頁  >  文章  >  後端開發  >  一個簡單的Go模擬—並發問題

一個簡單的Go模擬—並發問題

PHPz
PHPz轉載
2024-02-09 14:10:10669瀏覽

一個簡單的Go模擬—並發問題

php小編新一為大家帶來了一個簡單卻有趣的Go模擬遊戲,名為「並發問題」。這款遊戲以並發程式設計為主題,讓玩家在虛擬的世界中體驗並發程式設計的魅力。遊戲中,玩家需要透過編寫程式碼來處理多個任務的同時執行,測試自己的並發程式設計能力。遊戲介面簡潔明了,操作簡單,適合初學者入門,也提供了多個難度和挑戰模式供玩家選擇。無論你是初學者還是有一定經驗的開發者,都能在這個模擬遊戲中享受到並發程式設計的樂趣。

問題內容

我是來自波蘭的學生,這學期我開始了並發程式設計課程(Go、Ada 以及將來的一些理論和 CSP 語言)。說實話,Golang 看起來很有趣,但我有點困惑最重要的是,根據我的經驗,我稱自己為低於平均水平的程式設計師。基本上,我的任務是建立一個模擬,我將這樣描述:

  • 有一個 n*m 格

  • 可以隨機產生旅行者,最多 k 個旅行者,每個旅行者都有唯一的 ID(1、2、3 等等,最多 k)

  • 在隨機時刻,如果空間空閒(我確定空閒空間為 0),旅行者可以在網格上向上、向左、向右或向下移動

  • 還有一個鏡頭,有時會列印網格的當前狀態以及最近的移動(尚未實現)

  • 非正式地,我聽說我應該使用頻道,無論這意味著什麼

我的想法是創建一個帶有 id 和每個旅行者座標的結構,並將他們的 id 發送到表示移動意願的通道,然後我會隨機選擇移動方向。

我對並發有點困惑 - 不僅是我是否以及在哪裡應該使用 wgs 和互斥體,而且例如如果我執行 go func(){} 循環應該在內部還是外部。我會非常高興感謝任何提示、幫助或修復/想法來修復我的程式碼,因為目前,正如您所猜測的,它無法正常工作(例如,當相機打印網格時,有時會有超過k 個旅行者,其中多個旅行者共享相同的號碼,有時它們似乎消失了)。希望每個人都度過愉快的一天,我真的很感激任何幫助:)

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);
}
}

所有的想法都讓我有點不知所措——wgs、互斥體、原子等。

解決方法

  • 如果您想同時處理工作(例如拍攝相機快照和移動旅行者可以同時發生),goroutines 是輕量級執行緒。
  • 通道用於在 Go 例程之間傳輸資料。
  • 互斥體用於允許 goroutine 在共享資料上添加鎖以進行獨佔資料訪問,以避免競爭條件。

話雖如此:

  • 在一個 goroutine 中運行相機快照,同時讓旅行者在另一個 goroutine 中移動,看起來不錯。產生 Goroutine 是不必要的,您只需執行一次,因此您可以在主 Goroutine 中執行它。
  • 在您的案例中,頻道並沒有帶來任何好處。您有一個 Goroutine,它會產生一條訊息並將其通過通道發送到另一個將進行移動的 Goroutine。您可以在單一 goroutine 中按順序完成所有這些操作,並避免不必要的複雜性。通道對於不同的用例很有用,但在這裡它是多餘的。
  • 由於您有兩個 goroutine 存取共享記憶體(網格),因此您需要一個互斥體來避免競爭條件。每當其中一個運行時,它必須“鎖定”,完成工作,然後“解鎖”。另一個 goroutine 將在鎖定步驟處阻塞,直到第一個獲取鎖的 goroutine 解鎖。您可以使用讀取/寫入鎖定進一步優化它(僅相機需要讀鎖,而移動協程需要讀取/寫入鎖定)
  • 如果您想要更多隨機性,可以為每個旅行者創建一個 goroutine。

以上是一個簡單的Go模擬—並發問題的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:stackoverflow.com。如有侵權,請聯絡admin@php.cn刪除