基本
- スレッドとは何ですか?
スレッドは、プロセス内の実行パスです。 - なぜマルチスレッドなのか?
アイデアは、プロセスを複数のスレッドに分割することによって並列処理を実現することです。たとえば、ブラウザでは、複数のタブを異なるスレッドにすることができます。 - プロセスとスレッド
主な違いは、同じプロセス内のスレッドは共有メモリスペースで実行されるのに対し、プロセスは別々のメモリスペースで実行されることです。
Goroutine
Agoroutine
は、Goランタイムによって管理される軽量スレッドです。
go f(x, y, z) // starts a new goroutine by running f(x, y, z)
評価のはf
、x
、y
、z
現在のゴルーチンとで起こる実行のf
新しいゴルーチンで起こります。
ゴルーチンは同じアドレス空間で実行されるため、共有メモリへのアクセスを同期する必要があります。sync
他のプリミティブがあるとして、あなたが行くには非常にそれらを必要としませんが、パッケージには、便利なプリミティブを提供します。
- ケース1
func display(str string) {
time.Sleep(2 * time.Second)
fmt.Println(str)
}
func main() {
display("NORMAL")
go display("GOROUTINE")
}
出力:NORMAL
2。ケース2
func display(str string) {
time.Sleep(2 * time.Second)
fmt.Println(str)
}
func main() {
go display("GOROUTINE")
display("NORMAL")
}
出力:GOROUTINE\nNORMAL
3。ケース3
func display(str string) {
fmt.Println(str)
}
func main() {
go display("GOROUTINE")
display("NORMAL")
}
出力: NORMAL
チャンネル
チャネルは、チャネルオペレータと送受信できる型付きコンジットです<-
。
ch <- v // send v to channel ch
v := <- ch // receive from ch, and assign value to v
// like map and slice, channel must be created before use
ch := make(chan int)
1つのタスクを一緒に解決する2つのゴルーチンの例:
func sum(s []int, c chan int) {
sum := 0
for _, v := range s {
sum += v
}
c <- sum // send sum to c
}
func main() {
s := []int{
7, 2, 8, -9, 4, 0}
c := make(chan int)
go sum(s[:len(s)/2], c)
go sum(s[len(s)/2:], c)
x, y := <-c, <-c // receive from c
fmt.Println(x, y, x+y)
}
バッファリングされたチャネル
チャネルはバッファリングできます。
バッファリングされたmake
チャネルを初期化するための2番目の引数としてバッファ長を指定します。
ch := make(chan int, 100)
バッファがいっぱいの場合にのみ、バッファされたチャネルブロックに送信します。バッファが空のときにブロックを受け取ります。
func main() {
ch := make(chan int, 2)
ch <- 1
ch <- 2
ch <- 3 // deadlock, all goroutines are asleep
fmt.Println(<-ch)
fmt.Println(<-ch)
}
範囲と閉じる
送信者はチャネルを閉じることができます。
ch := make(chan int, 2)
ch <- 1
ch <- 2
close(ch)
受信者は、チャネルが閉じているかどうかをテストできます。
v, ok := <-ch
ループfor i := range ch
は、チャネルch
が閉じるまでチャネルから値を繰り返し受け取ります。
閉じたチャネルで送信すると、が発生しpanic
ます。
チャネルはファイルとは異なります。通常、それらを閉じる必要はありません。range
ループを終了するなど、これ以上値が来ないことを受信者に通知する必要がある場合にのみ、閉じる必要があります。
func fibonacci(n int, c chan int) {
x, y := 0, 1
for i := 0; i < n; i++ {
c <- x
x, y = y, x+y
}
close(c)
}
func main() {
c := make(chan int, 10)
go fibonacci(cap(c), c)
for i := range c {
fmt.Println(i)
}
}
選択する
このselect
ステートメントにより、ゴルーチンは複数の通信操作(c <-xなど)を待機します。ブロックを実行することができ、その例1まで、それはそのケースを実行します。複数の準備ができている場合は、ランダムに1つを選択します。select
func fibonacci(c, quit chan int) {
x, y := 0, 1
for {
select {
case c <- x:
x, y = y, x+y
case <-quit:
fmt.Println("quit")
return
}
}
}
func main() {
c := make(chan int)
quit := make(chan int)
go func() {
for i := 0; i < 10; i++ {
fmt.Println(<-c)
}
quit <- 0
}()
fibonacci(c, quit)
}
匿名のゴルーチン関数の構文:
go func() {
// function body
}()
default
aのケースは、select
他のケースの準備ができていない場合に実行されます。
sync.Mutex
// SafeCounter is safe to use concurrently.
type SafeCounter struct {
mu sync.Mutex // make sure only one goroutine can access a variable at a time to avoid conflicts
v map[string]int
}
// Inc increments the counter for the given key.
func (c *SafeCounter) Inc(key string) {
// define a block of code to be executed in mutual exclusion by surrounding it with a call to Lock and Unlock
c.mu.Lock()
// Lock so only one goroutine at a time can access the map c.v.
c.v[key]++
c.mu.Unlock()
}
// Value returns the current value of the counter for the given key.
func (c *SafeCounter) Value(key string) int {
c.mu.Lock()
// Lock so only one goroutine at a time can access the map c.v.
defer c.mu.Unlock() // use defer to ensure the mutex will be unlocked
return c.v[key]
}
func main() {
c := SafeCounter{
v: make(map[string]int)}
for i := 0; i < 1000; i++ {
go c.Inc("somekey")
}
fmt.Println(c.Value("somekey"))
}