インターネットの急速な発展に伴い、大規模分散システムに対する需要はますます高まっており、同時プログラミングと並列コンピューティングはインターネット開発者が習得しなければならないスキルとなっています。 Go 言語は同時実行をサポートするために生まれた言語であり、同時プログラミングと並列コンピューティングで非常に優れたパフォーマンスを発揮します。この記事では、Go 言語の同時実行モードと並列コンピューティングを紹介し、読者が深く理解できるようにいくつかの実践的なケースを示します。
1. Go 言語の同時実行モード
Go 言語の同時実行モードは、主に goroutine とチャネルの 2 つの基本コンポーネントに基づいています。 Goroutine は軽量のスレッドで、Go 言語のランタイム システムによって管理され、go キーワードを通じて開始でき、Goroutine はチャネルを通じて通信できます。
次は、ゴルーチンとチャネルの簡単な例です。
package main import "fmt" func printMsg(msg string, ch chan string) { ch <- msg } func main() { ch := make(chan string) msgs := []string{"Hello", "Golang", "Parallel"} for _, msg := range msgs { go printMsg(msg, ch) } for i := 0; i < len(msgs); i++ { fmt.Println(<-ch) } }
コードは、for ループを通じて 3 つのゴルーチンを開始し、それぞれ 3 つの文字列を出力します。 printMsg 関数は文字列メッセージをチャネルに書き込み、main 関数は再びチャネルから読み取ります。
1.1 パイプライン モード
Go 言語では、パイプライン モードを通じて複数のゴルーチンを直列に接続して、より複雑な同時システムを形成できます。パイプライン パターンの実装は通常、複数のゴルーチン間のチャネル通信を通じて行われ、あるゴルーチンから別のゴルーチンにデータを渡し、各ゴルーチンでデータを処理および変換します。以下は簡単なパイプライン モードの例です:
package main import ( "fmt" ) func addOne(in <-chan int, out chan<- int) { for val := range in { out <- val + 1 } close(out) } func printNums(out <-chan int) { for val := range out { fmt.Println(val) } } func main() { nums := []int{1, 2, 3} in := make(chan int) out := make(chan int) go addOne(in, out) go printNums(out) for _, num := range nums { in <- num } close(in) }
コード パスは 3 つのゴルーチン、つまり入力ゴルーチン、および 1 つの処理ゴルーチンと出力ゴルーチンを定義します。addOne 関数は、入力チャネルのデータに 1 を加算して書き込みます。チャネルでは、printNums 関数が出力チャネルからデータを読み取り、出力します。
1.2 選択パターン
Go 言語の select ステートメントは、複数のチャネルを扱うための便利な方法、つまり選択パターン (select pattern) を提供します。選択モードを使用すると、複数のチャネルでノンブロッキング選択操作を実行でき、複数のチャネルに読み取りまたは書き込み可能なメッセージがある場合、そのうちの 1 つが自動的に選択されて操作されます。
以下は簡単な選択モードの例です:
package main import "fmt" func ping(ch chan<- string) { for { ch <- "ping" } } func pong(ch chan<- string) { for { ch <- "pong" } } func printer(ch <-chan string) { for { fmt.Println(<-ch) } } func main() { ch1 := make(chan string) ch2 := make(chan string) ch3 := make(chan string) go ping(ch1) go pong(ch2) go printer(ch3) for { select { case msg := <-ch1: ch3 <- msg case msg := <-ch2: ch3 <- msg } } }
コードでは、ping 関数と pong 関数がそれぞれ ch1 と ch2、およびプリンターに「ping」と「pong」メッセージを送信します。関数はch3のメッセージを読み出して出力します。 main関数ではselect文でch1とch2のメッセージを監視し、受信したメッセージをch3経由でプリンタ関数に渡して出力します。
2. Go 言語の並列コンピューティング
Go 言語の組み込み並列コンピューティング モジュールには、同期、アトミック、コンテキストなどが含まれます。 sync と atomic は主にミューテックス (Mutex) とアトミック操作 (アトミック操作) を使用して同時データアクセスを制御し、コンテキストはゴルーチンのコンテキスト情報を管理するために使用されます。これらのモジュールの使用方法を簡単に紹介します。
2.1 ミューテックス ロック
ミューテックス ロックは、共有リソースを保護するために最も一般的に使用される同期メカニズムの 1 つであり、最もよく使用される同期メカニズムでもあります。 Go 言語の基本的な同期メカニズム。 Go 言語では、同期パッケージの Mutex タイプを使用してミューテックス ロックを作成できます。 Mutex タイプは、Lock と Unlock という 2 つの重要なメソッドを提供します。共有リソースにアクセスする前に、Lock メソッドを呼び出してロックを取得し、アクセスの完了後に Unlock メソッドを呼び出してロックを解放する必要があります。以下はミューテックス ロックの簡単な例です:
package main import ( "fmt" "sync" ) func addOne(num *int, mutex *sync.Mutex, wg *sync.WaitGroup) { mutex.Lock() *num += 1 mutex.Unlock() wg.Done() } func main() { var wg sync.WaitGroup var num int mutex := &sync.Mutex{} for i := 0; i < 1000; i++ { wg.Add(1) go addOne(&num, mutex, &wg) } wg.Wait() fmt.Println(num) }
コードでは、addOne 関数は num 変数に 1 を追加するように定義されています。1 を追加する前に、最初にミューテックス ロックを取得し、その後にミューテックス ロックを取得する必要があります。 1を加えると、ミューテックスロックを解除する必要があります。 WaitGroup を使用して、すべてのゴルーチンが実行を完了し、最終結果を出力するのを待ちます。
2.2 アトミック操作
同時実行性が高いシナリオでは、ミューテックス ロックによってプログラムのパフォーマンスが低下する可能性があるため、Go 言語はミューテックス ロックを置き換えるアトミック操作を提供します。アトミック パッケージは、AddInt64、CompareAndSwapInt64、SwapInt64 など、いくつかのアトミック操作関数を提供します。アトミック操作を使用すると、変数に対する操作が他のゴルーチンによって中断されず、同時実行が影響を受けなくなります。以下は簡単なアトミック操作の例です:
package main import ( "fmt" "sync/atomic" ) func addOne(num *int64, count *int64, done chan bool) { for i := 0; i < 1000; i++ { atomic.AddInt64(num, 1) } atomic.AddInt64(count, 1) done <- true } func main() { var num int64 var count int64 done := make(chan bool) for i := 0; i < 100; i++ { go addOne(&num, &count, done) } for i := 0; i < 100; i++ { <-done } fmt.Printf("num=%d, count=%d ", num, count) }
コードでは、アトミック パッケージの AddInt64 関数を使用して、num 変数に対してアトミック操作が実行されます。操作が完了すると、メイン スレッドは次の方法で通知されます。終わり。通常のAddInt64関数を通じてcount変数を累積し、最終的にnumとcountの値を出力します。
2.3 コンテキスト管理
Go 言語では、リクエスト ID やタイムアウト設定などのコンテキスト情報を複数のゴルーチン間で渡すことが必要になることがよくあります。 context パッケージは、Goroutine コンテキスト情報を管理する便利な方法を提供します。コンテキストを使用する場合、通常はメインのゴルーチン内に親コンテキストを作成する必要がありますが、ゴルーチンの導出時にはWithCancel、WithDeadline、WithValueなどの関数を使用して子コンテキストを作成し、対応するコンテキスト情報を渡します。以下は、単純なコンテキスト管理の例です。
package main import ( "context" "fmt" "time" ) func worker(ctx context.Context, id int) { for { select { case <-ctx.Done(): fmt.Printf("worker %d canceled ", id) return default: fmt.Printf("worker %d is working ", id) time.Sleep(1 * time.Second) } } } func main() { ctx, cancel := context.WithCancel(context.Background()) for i := 0; i < 3; i++ { go worker(ctx, i) } time.Sleep(5 * time.Second) cancel() }
コードでは、コンテキスト パッケージを使用して親コンテキストを作成し、WithCancel 関数を使用して子コンテキストを作成します。ワーカー関数では、select ステートメントを使用して ctx.Done() のシグナルをリッスンします。ctx.Done() が閉じられると、コンテキストがキャンセルされ、ワーカー関数を終了する必要があることを意味します。 main 関数で、cancel 関数を使用してサブコンテキストを閉じ、サブコンテキストがキャンセルされるのを待ちます。実行結果は次のとおりです。
worker 0 is working worker 1 is working worker 2 is working worker 2 canceled worker 1 canceled worker 0 canceled
親コンテキストがキャンセルされると、すべての子コンテキストが通知を受け取り、実行を終了します。
3.結論
この記事では、Go 言語の同時実行モードと並列コンピューティングを簡単に紹介し、ゴルーチン、チャネル、ミューテックス ロック、アトミック操作、コンテキストなどの基本的なコンポーネントとモジュールを紹介します。これらの基本知識を学ぶことで、Go 言語の同時実行性と並列プログラミングをより適切に習得し、高性能で同時実行性の高いインターネット アプリケーションを構築するための基礎を築くことができます。
以上がGo言語の同時実行モードと並列コンピューティングをマスターするの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。