Ghi chú:
Kể từ Go 1.5, GOMAXPROCS được đặt thành số lõi của phần cứng: golang.org/doc/go1.5#runtime , thấp hơn câu trả lời ban đầu trước 1.5.
Khi bạn chạy chương trình Go mà không chỉ định biến môi trường GOMAXPROCS, các goroutines Go được lên lịch thực thi trong một chuỗi hệ điều hành duy nhất. Tuy nhiên, để làm cho chương trình có vẻ đa luồng (đó là mục đích của các goroutines, phải không?), Bộ lập lịch Go đôi khi phải chuyển đổi bối cảnh thực thi, vì vậy mỗi goroutine có thể thực hiện phần việc của nó.
Như tôi đã nói, khi biến GOMAXPROCS không được chỉ định, Go runtime chỉ được phép sử dụng một luồng, vì vậy không thể chuyển đổi ngữ cảnh thực thi trong khi goroutine đang thực hiện một số công việc thông thường, như tính toán hoặc thậm chí IO (được ánh xạ tới các hàm C đơn giản ). Ngữ cảnh chỉ có thể được chuyển đổi khi sử dụng nguyên mẫu đồng thời của Go, ví dụ: khi bạn bật một số lệnh hoặc (đây là trường hợp của bạn) khi bạn yêu cầu người lập lịch chuyển đổi ngữ cảnh một cách rõ ràng - đây runtime.Gosched
là mục đích.
Vì vậy, trong ngắn hạn, khi ngữ cảnh thực thi trong một goroutine gặp Gosched
lệnh gọi, bộ lập lịch được hướng dẫn chuyển việc thực thi sang một goroutine khác. Trong trường hợp của bạn, có hai goroutines, main (đại diện cho luồng 'chính' của chương trình) và bổ sung, một trong những thứ bạn đã tạo go say
. Nếu bạn xóa Gosched
cuộc gọi, ngữ cảnh thực thi sẽ không bao giờ được chuyển từ quy trình đầu tiên sang quy trình thứ hai, do đó không có 'thế giới' cho bạn. Khi Gosched
có mặt, bộ lập lịch chuyển việc thực thi trên mỗi lần lặp vòng lặp từ goroutine đầu tiên sang chương trình thứ hai và ngược lại, vì vậy bạn có 'hello' và 'world' xen kẽ.
FYI, đây được gọi là 'đa nhiệm hợp tác': các goroutines phải nhường quyền kiểm soát rõ ràng cho các goroutines khác. Cách tiếp cận được sử dụng trong hầu hết các hệ điều hành hiện đại được gọi là 'đa nhiệm phủ đầu': các luồng thực thi không liên quan đến việc chuyển điều khiển; thay vào đó, bộ lập lịch chuyển đổi các ngữ cảnh thực thi sang chúng một cách minh bạch. Cách tiếp cận hợp tác thường được sử dụng để triển khai 'chuỗi màu xanh lá cây', tức là các chuỗi điều chỉnh đồng thời hợp lý không ánh xạ 1: 1 tới các chuỗi hệ điều hành - đây là cách thực hiện thời gian chạy Go và các tuyến tính của nó.
Cập nhật
Tôi đã đề cập đến biến môi trường GOMAXPROCS nhưng không giải thích nó là gì. Đã đến lúc khắc phục điều này.
Khi biến này được đặt thành một số dương N
, Go runtime sẽ có thể tạo tối đa N
các luồng gốc, trên đó tất cả các luồng màu xanh lục sẽ được lên lịch. Chủ đề riêng là một loại luồng được tạo bởi hệ điều hành (luồng Windows, pthreads, v.v.). Điều này có nghĩa là nếu N
lớn hơn 1, có thể các goroutines sẽ được lên lịch để thực thi trong các luồng gốc khác nhau và do đó, chạy song song (ít nhất là tùy theo khả năng máy tính của bạn: nếu hệ thống của bạn dựa trên bộ xử lý đa lõi, nó có khả năng là các luồng này sẽ thực sự song song; nếu bộ xử lý của bạn có lõi đơn, thì đa nhiệm ưu tiên được thực hiện trong các luồng hệ điều hành sẽ tạo ra khả năng thực thi song song).
Có thể đặt biến GOMAXPROCS bằng cách sử dụng runtime.GOMAXPROCS()
hàm thay vì đặt trước biến môi trường. Sử dụng một cái gì đó như thế này trong chương trình của bạn thay vì hiện tại main
:
func main() {
runtime.GOMAXPROCS(2)
go say("world")
say("hello")
}
Trong trường hợp này, bạn có thể quan sát kết quả thú vị. Có thể bạn sẽ nhận được các dòng 'xin chào' và 'thế giới' được in xen kẽ không đồng đều, ví dụ:
hello
hello
world
hello
world
world
...
Điều này có thể xảy ra nếu các goroutines được lên lịch để phân tách các chuỗi hệ điều hành. Trên thực tế, đây là cách hoạt động của đa nhiệm phủ đầu (hoặc xử lý song song trong trường hợp hệ thống đa lõi): các luồng song song và đầu ra kết hợp của chúng là không xác định. BTW, bạn có thể rời khỏi hoặc xóa Gosched
cuộc gọi, nó dường như không có tác dụng khi GOMAXPROCS lớn hơn 1.
Sau đây là những gì tôi nhận được trong một số lần chạy chương trình với runtime.GOMAXPROCS
cuộc gọi.
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
Hãy xem, đôi khi đầu ra là tốt, đôi khi không. Chủ nghĩa không xác định trong hành động :)
Cập nhật khác
Có vẻ như trong các phiên bản mới hơn của trình biên dịch Go Thời gian chạy Go buộc các goroutines không chỉ mang lại hiệu quả khi sử dụng nguyên thủy đồng thời mà còn trên các lệnh gọi hệ điều hành. Điều này có nghĩa là ngữ cảnh thực thi có thể được chuyển đổi giữa các goroutines trên các lệnh gọi hàm IO. Do đó, trong các trình biên dịch Go gần đây, có thể quan sát hành vi không xác định ngay cả khi GOMAXPROCS không được đặt hoặc được đặt thành 1.