Rumah  >  Artikel  >  pembangunan bahagian belakang  >  Simulasi Go yang mudah - isu konkurensi

Simulasi Go yang mudah - isu konkurensi

PHPz
PHPzke hadapan
2024-02-09 14:10:10669semak imbas

Simulasi Go yang mudah - isu konkurensi

editor php Xinyi membawakan anda permainan simulasi Go yang ringkas tetapi menarik yang dipanggil "Masalah Concurrency". Permainan ini mengambil pengaturcaraan serentak sebagai temanya, membolehkan pemain mengalami daya tarikan pengaturcaraan serentak dalam dunia maya. Dalam permainan, pemain perlu menulis kod untuk mengendalikan pelaksanaan serentak pelbagai tugas dan menguji kebolehan pengaturcaraan serentak mereka. Antara muka permainan adalah ringkas dan jelas, dan operasinya mudah, sesuai untuk pemula untuk bermula, dan ia juga menyediakan pelbagai mod kesukaran dan cabaran untuk dipilih oleh pemain. Sama ada anda seorang pemula atau pembangun yang berpengalaman, anda boleh menikmati keseronokan pengaturcaraan serentak dalam permainan simulasi ini.

Kandungan soalan

Saya seorang pelajar dari Poland dan semester ini saya memulakan kursus pengaturcaraan serentak (Go, Ada dan beberapa bahasa teori dan CSP pada masa hadapan). Sejujurnya, Golang kelihatan menarik tetapi saya agak keliru. Kesimpulannya ialah dalam pengalaman saya, saya akan memanggil diri saya seorang pengaturcara di bawah purata. Pada asasnya, tugas saya adalah untuk mencipta simulasi, yang akan saya huraikan seperti ini:

  • Terdapat grid n*m

  • Pengembara boleh dijana secara rawak, sehingga k pengembara, setiap pengembara mempunyai ID unik (1, 2, 3, dsb., sehingga k)

  • Secara rawak, jika ruang kosong (saya pasti ruang kosong ialah 0), pengembara boleh bergerak ke atas, kiri, kanan atau bawah pada grid

  • Juga mempunyai kamera yang kadangkala mencetak keadaan semasa mesh serta pergerakan terkini (belum dilaksanakan)

  • Secara tidak rasmi, saya pernah dengar saya patut menggunakan saluran, apa pun maksudnya

Idea saya adalah untuk mencipta struktur dengan id dan koordinat setiap pengembara dan menghantar id mereka ke saluran yang mewakili kesediaan untuk bergerak, dan kemudian saya akan memilih arah pergerakan secara rawak.

Saya agak keliru tentang concurrency - bukan sahaja jika dan di mana saya harus menggunakan wgs dan mutex, tetapi juga sebagai contoh jika saya pergi func(){} sekiranya gelung berada di dalam atau di luar. Saya amat berbesar hati untuk menghargai sebarang petua, bantuan atau pembetulan/idea untuk membetulkan kod saya kerana pada masa ini, seperti yang anda sangka, ia tidak berfungsi dengan betul (cth. apabila kamera mencetak grid, kadangkala terdapat lebih daripada k pengembara , di mana berbilang pengembara berkongsi nombor yang sama dan kadangkala mereka kelihatan hilang). Harap semua orang mempunyai hari yang hebat dan saya sangat menghargai sebarang bantuan :)

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

Saya agak terharu dengan semua idea - wg, mutex, atom, dll.

Penyelesaian

  • Jika anda ingin memproses kerja secara serentak (cth. mengambil syot kilat kamera dan menggerakkan pengembara boleh berlaku pada masa yang sama), goroutine ialah benang yang ringan.
  • Saluran digunakan untuk memindahkan data antara rutin Go.
  • Mutex digunakan untuk membenarkan gorout menambah kunci pada data kongsi untuk akses data eksklusif bagi mengelakkan keadaan perlumbaan.

Bahawa dikatakan:

  • Menjalankan syot kilat kamera dalam satu goroutine sambil meminta pengembara bergerak dalam goroutine lain kelihatan bagus. Melahirkan Goroutine adalah tidak perlu, anda hanya perlu melakukannya sekali, supaya anda boleh melaksanakannya dalam Goroutine utama.
  • Dalam kes anda, saluran tidak membawa manfaat. Anda mempunyai Goroutine yang menjana mesej dan menghantarnya melalui saluran ke Goroutine lain yang akan melakukan pergerakan. Anda boleh melakukan semua ini secara berurutan dalam satu goroutine dan mengelakkan kerumitan yang tidak perlu. Saluran berguna untuk kes penggunaan yang berbeza, tetapi di sini ia adalah berlebihan.
  • Memandangkan anda mempunyai dua gorout mengakses memori kongsi (grid), anda memerlukan mutex untuk mengelakkan keadaan perlumbaan. Setiap kali salah satu daripada ini dijalankan, ia mesti "mengunci", menyelesaikan kerjanya, dan kemudian "membuka kunci". Satu lagi goroutine akan menyekat pada langkah penguncian sehingga goroutine pertama yang memperoleh kunci itu terbuka. Anda boleh mengoptimumkannya lagi menggunakan kunci baca/tulis (kunci baca hanya diperlukan untuk kamera dan kunci baca/tulis untuk coroutine mudah alih)
  • Jika anda mahukan lebih rawak, anda boleh membuat goroutine untuk setiap pengembara.

Atas ialah kandungan terperinci Simulasi Go yang mudah - isu konkurensi. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Artikel ini dikembalikan pada:stackoverflow.com. Jika ada pelanggaran, sila hubungi admin@php.cn Padam