catatan:
Mulai Go 1.5, GOMAXPROCS disetel ke jumlah inti perangkat keras: golang.org/doc/go1.5#runtime , di bawah jawaban asli sebelum 1.5.
Saat Anda menjalankan program Go tanpa menentukan variabel lingkungan GOMAXPROCS, goroutine Go dijadwalkan untuk dieksekusi di thread OS tunggal. Namun, untuk membuat program tampak multithread (itulah gunanya goroutine, bukan?), Penjadwal Go terkadang harus mengganti konteks eksekusi, sehingga setiap goroutine dapat melakukan tugasnya.
Seperti yang saya katakan, ketika variabel GOMAXPROCS tidak ditentukan, runtime Go hanya diperbolehkan untuk menggunakan satu utas, jadi tidak mungkin untuk mengganti konteks eksekusi saat goroutine melakukan beberapa pekerjaan konvensional, seperti komputasi atau bahkan IO (yang dipetakan ke fungsi C biasa ). Konteks dapat dialihkan hanya ketika Go concurrency primitif digunakan, misalnya ketika Anda mengaktifkan beberapa chans, atau (ini kasus Anda) ketika Anda secara eksplisit memberi tahu penjadwal untuk mengganti konteks - runtime.Gosched
untuk itulah.
Jadi, singkatnya, ketika konteks eksekusi dalam satu goroutine mencapai Gosched
panggilan, penjadwal diinstruksikan untuk mengalihkan eksekusi ke goroutine lain. Dalam kasus Anda, ada dua goroutine, main (yang mewakili utas 'main' dari program) dan tambahan, yang telah Anda buat go say
. Jika Anda menghapus Gosched
panggilan, konteks eksekusi tidak akan pernah ditransfer dari goroutine pertama ke goroutine kedua, karenanya tidak ada 'dunia' untuk Anda. Saat Gosched
ada, penjadwal mentransfer eksekusi pada setiap iterasi loop dari goroutine pertama ke yang kedua dan sebaliknya, jadi Anda memiliki 'hello' dan 'world' interleaved.
FYI, ini disebut 'multitasking kooperatif': goroutine harus secara eksplisit menghasilkan kontrol ke goroutine lain. Pendekatan yang digunakan di sebagian besar OS kontemporer disebut 'multitasking preemptive': thread eksekusi tidak peduli dengan transfer kontrol; penjadwal mengalihkan konteks eksekusi secara transparan kepada mereka. Pendekatan kooperatif sering digunakan untuk mengimplementasikan 'utas hijau', yaitu coroutine konkuren logis yang tidak memetakan 1: 1 ke utas OS - ini adalah cara runtime Go dan penerapan goroutine-nya.
Memperbarui
Saya telah menyebutkan variabel lingkungan GOMAXPROCS tetapi tidak menjelaskan apa itu. Saatnya memperbaiki ini.
Jika variabel ini disetel ke angka positif N
, waktu proses Go akan dapat membuat hingga N
utas asli, di mana semua utas hijau akan dijadwalkan. Utas asli sejenis utas yang dibuat oleh sistem operasi (utas Windows, pthreads, dll.). Ini berarti bahwa jika N
lebih besar dari 1, mungkin goroutine akan dijadwalkan untuk dijalankan di utas asli yang berbeda dan, akibatnya, berjalan secara paralel (setidaknya, sesuai dengan kemampuan komputer Anda: jika sistem Anda didasarkan pada prosesor multi inti, itu kemungkinan utas ini akan benar-benar paralel; jika prosesor Anda memiliki inti tunggal, maka multitasking preemptive yang diterapkan di utas OS akan membuat visibilitas eksekusi paralel).
Dimungkinkan untuk menyetel variabel GOMAXPROCS menggunakan runtime.GOMAXPROCS()
fungsi alih-alih menyetel sebelumnya variabel lingkungan. Gunakan sesuatu seperti ini di program Anda, bukan yang saat ini main
:
func main() {
runtime.GOMAXPROCS(2)
go say("world")
say("hello")
}
Dalam hal ini Anda dapat mengamati hasil yang menarik. Ada kemungkinan bahwa Anda akan mendapatkan garis 'halo' dan 'dunia' tercetak berselang-seling tidak merata, mis
hello
hello
world
hello
world
world
...
Ini bisa terjadi jika goroutine dijadwalkan untuk memisahkan utas OS. Ini sebenarnya adalah cara kerja multitasking preemptive (atau pemrosesan paralel dalam kasus sistem multicore): utas paralel, dan output gabungannya tidak pasti. BTW, Anda dapat meninggalkan atau menghapus Gosched
panggilan, sepertinya tidak akan berpengaruh jika GOMAXPROCS lebih besar dari 1.
Berikut ini adalah apa yang saya dapatkan dari beberapa program dengan runtime.GOMAXPROCS
panggilan.
hyperplex /tmp % go run test.go
hello
hello
hello
world
hello
world
hello
world
hyperplex /tmp % go run test.go
hello
world
hello
world
hello
world
hello
world
hello
world
hyperplex /tmp % go run test.go
hello
hello
hello
hello
hello
hyperplex /tmp % go run test.go
hello
world
hello
world
hello
world
hello
world
hello
world
Lihat, terkadang keluarannya bagus, terkadang tidak. Indeterminisme beraksi :)
Pembaruan lainnya
Sepertinya, dalam versi terbaru kompilator Go, waktu proses Go memaksa goroutine untuk menghasilkan tidak hanya penggunaan primitif konkurensi, tetapi juga pada panggilan sistem OS. Ini berarti bahwa konteks eksekusi dapat dialihkan antar goroutine juga pada pemanggilan fungsi IO. Akibatnya, di kompiler Go baru-baru ini, dimungkinkan untuk mengamati perilaku tak tentu bahkan ketika GOMAXPROCS tidak disetel atau disetel ke 1.
Gosched()
diperlukan atau tidaknya tergantung pada program Anda, tidak tergantung padaGOMAXPROCS
nilai. Efisiensi multitasking preemptive di atas koperasi juga tergantung pada program Anda. Jika program Anda terikat I / O, maka multitasking kooperatif dengan I / O asinkron mungkin akan lebih efisien (yaitu memiliki lebih banyak throughput) daripada I / O berbasis thread sinkron; jika program Anda terikat dengan CPU (mis. komputasi panjang), maka multitasking kooperatif akan menjadi kurang berguna.Penjadwalan kerjasama adalah pelakunya. Tanpa menghasilkan, goroutine lain (katakanlah "dunia") mungkin secara legal mendapatkan kesempatan nol untuk mengeksekusi sebelum / ketika main berakhir, yang menurut spesifikasi menghentikan semua gorutine - mis. seluruh proses.
sumber
runtime.Gosched()
hasilnya. Apa artinya? Ini menghasilkan kontrol kembali ke fungsi utama?