質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.47%
Go

Go(golang)は、Googleで開発されたオープンソースのプログラミング言語です。

Q&A

解決済

1回答

1322閲覧

golang の mutex で排他制御がかからない

pekuchan

総合スコア1

Go

Go(golang)は、Googleで開発されたオープンソースのプログラミング言語です。

0グッド

0クリップ

投稿2020/09/24 10:59

下記のコードで、m がロックされません。
なぜですか?

mutex := sync.Mutex
eg := errgroup.Group{}
m := make(map[string][]string)
for 条件 {
eg.Go(func() error {
mutex.Lock()
m に追加
mutex.Unlock
return nil
})
}
eg.Wait()

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

errgroup パッケージの使い方はあっています。上記のコードはローカル環境でコンパイルできましたか?(条件m に追加 などを想定通り記述した上で)

以下は上記のコードに少しだけ手入れして、動作するようにしたコード例です。

  • main.go

go

1package main 2 3import ( 4 "fmt" 5 "strconv" 6 "sync" 7 8 "golang.org/x/sync/errgroup" 9) 10 11func main() { 12 var mu sync.Mutex 13 eg := errgroup.Group{} 14 m := make(map[string][]string) 15 for i := 0; i < 10; i++ { 16 i := i 17 eg.Go(func() error { 18 mu.Lock() 19 m[strconv.Itoa(i)] = []string{strconv.Itoa(i * 100)} 20 mu.Unlock() 21 return nil 22 }) 23 } 24 eg.Wait() 25 26 fmt.Printf("m: %+v", m) 27}

https://play.golang.org/p/EgRFZxCWyVE

  • 出力結果
m: map[0:[0] 1:[100] 2:[200] 3:[300] 4:[400] 5:[500] 6:[600] 7:[700] 8:[800] 9:[900]]

追記

for loop上の変数をgoroutineで並行処理する場合には注意が必要です。以下のコードは正しく動きません。

go

1package main 2 3import ( 4 "fmt" 5 6 "golang.org/x/sync/errgroup" 7) 8 9func main() { 10 g := errgroup.Group{} 11 s := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} 12 13 for _, v := range s { 14 // v := v としてループごとに独立したアドレスで値を確保する必要があります 15 g.Go(func() error { 16 fmt.Printf("value: %d\n", v) 17 return nil 18 }) 19 } 20 21 g.Wait() 22}

上記の for range 内で宣言している値のアドレスはループごとに独立ではなく、 同じ アドレスを共有しています。そして、それぞれのgoroutineではクロージャで変数の "アドレス" を参照しています。つまりgoroutineで変数の値を参照するときに競合状態になっています。

ループごとの独立した変数のアドレスを確保しなおす必要があります。

投稿2020/09/24 11:37

編集2020/09/28 04:02
d_tutuz

総合スコア730

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

d_tutuz

2020/09/24 11:40

ちなみに eg.Wait() の戻り値の error はチェックしたほうが良いです。(上記のサンプルコードでは省いていますが)
pekuchan

2020/09/24 11:56

ありがとうございます。 error はチェックするようにします。 コンパイルは通っています。 どこがポイントだったのでしょうか? 申し訳ございません。 何が原因だったのか、違いが分かっていません。 ちなみに、m への追加は m[stringA] = append(m[stringA], string〜) としています。 この辺のハッキリ書かない部分に問題があるのかもしれません。
d_tutuz

2020/09/24 12:52

errgroup パッケージと sync パッケージの使い方は問題ないと思います。 m[stringA] = append(m[stringA], string〜) のようなmapのvalueのsliceにappendする動作も問題なく動作すると思いますが、むしろpekuchanさんが「m がロックされません。」と思われた理由はどこにありますか??
d_tutuz

2020/09/24 12:56

Go Playgroundで動作するようなサンプルがあれば、もう少し的確なコメントができるかもしれません。
pekuchan

2020/09/24 21:15

ありがとうございます。 やはり sync の使い方以外のところに問題があるのかもしれません。 実際に動作しているコードを準備します。(少し時間がかかります) > 「m がロックされません。」と思われた理由はどこにありますか?? ループを抜けた後で、m を表示しました。 そうしますと、各ループでそれぞれ設定されると想定していた値が、1ループ分と思われる値しか設定されていませんでしたので、そう判断しました。 しかし、まずそもそも、gotoutine を外して本当に値が設定されるか、を見てみます。 お手数をお掛けして申し訳ございません。
pekuchan

2020/09/25 10:19

こちらがそのコードです。 No Goroutine と Goroutine とで結果が異なります。 func test() error { type s struct { m1 string `json:"m_1"` } var stringListInStruct map[*s][]string var resultMap map[*s][]string var mutex = &sync.Mutex{} s1 := s { "AAAAA" } s2 := s { "BBBBB" } s3 := s { "CCCCC" } stringListInStruct = make(map[*s][]string) stringListInStruct[&s1] = append(stringListInStruct[&s1], "11111") stringListInStruct[&s1] = append(stringListInStruct[&s1], "22222") stringListInStruct[&s1] = append(stringListInStruct[&s1], "33333") stringListInStruct[&s2] = append(stringListInStruct[&s2], "11111") stringListInStruct[&s2] = append(stringListInStruct[&s2], "22222") stringListInStruct[&s3] = append(stringListInStruct[&s3], "22222") g := errgroup.Group{} util.WriteLog(util.I, "No Goroutine") resultMap = make(map[*s][]string) for k, v := range stringListInStruct { for _, v2 := range v { resultMap[k] = append(resultMap[k], v2) } } for k, v := range resultMap { util.WriteLog(util.I, fmt.Sprintf("k[%s] v[%s]", k.m1, v)) } util.WriteLog(util.I, "Goroutine") resultMap = make(map[*s][]string) for k, v := range stringListInStruct { g.Go(func() error { mutex.Lock() for _, v2 := range v { resultMap[k] = append(resultMap[k], v2) } mutex.Unlock() return nil }) } g.Wait() for k, v := range resultMap { util.WriteLog(util.I, fmt.Sprintf("k[%s] v[%s]", k.m1, v)) } return nil }
d_tutuz

2020/09/25 11:44 編集

Goのfor loop上の変数をgorutineで並行処理する場合には注意が必要です。 for k, v := range stringListInStruct { のk, vはループごとに独立ではなく同じアドレスを共有しています。そして、それぞれのgoroutineではクロージャで変数の "アドレス" を参照しています。つまりgoroutineで変数の値を参照するときに競合状態になっています。 いただいたコードをループ内で独立した変数を確保して正しく動くようにしたものを記述しました。ご確認ください。 https://play.golang.org/p/JJRWroyM30Q ------ 私の記事ではないですが、「Goのforとgoroutineでやりがちなミスとたった一つの冴えたgo vetと」のような記事も参考になると思います。 https://qiita.com/sudix/items/67d4cad08fe88dcb9a6d
pekuchan

2020/09/25 12:38

ありがとうございました。 私の情報が足りずにお手数をお掛けしました。 とても親切に教えていただき感謝いたします。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.47%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問