2017-04-17 9 views
3

HTTPサーバーを作成して実行する非常に基本的なGolangアプリケーションがあります。サーバーには、データを送信するためのものと受信するものの2つのエンドポイントがあります。マップから同時に読み書きすると競合が発生する可能性があります。

サーバーへのPOST要求ごとに、本体からの着信データを解析し、チャネルにプッシュします。私は、チャンネルから読み込んでデータを地図に保存する機能を持っています。

JSONは、サーバーへのGET要求ごとにマップをマーシャリングしてクライアントに送信します。

HTTPサーバーに対する各要求は非同期で実行され、ワー​​カー関数は独自のゴルーチンで同期してマップに書き込みます。次のように

基本的な擬似コードは次のとおりです。

package main 

import (
    "net/http" 
) 

type dataStore map[string]string 

func listenHandler(stream chan string) http.HandlerFunc { 
    return func(w http.ResponseWriter, r *http.Request) { 
     // JSON unmarshal request body 

     // push data onto stream 
     stream <- data 
    } 
} 

func serveHandler(store *dataStore) http.HandlerFunc { 
    return func(w http.ResponseWriter, r *http.Request) { 
     // JSON marshal data store 
     // serve to client 
    } 
} 

func worker(stream <-chan string) *dataStore { 
    store := make(dataStore) 
    go func() { 
     for data := range stream { 
      // process and write data to map 
     } 
    }() 
    return &store 
} 

func main() { 
    stream := make(chan string) 
    store := worker(stream) 

    http.HandleFunc("/listen", listenHandler(stream)) 
    http.HandleFunc("/serve", serveHandler(store)) 

    http.ListenAndServe(":8080", nil) 
} 

私がテストし、問題なくアプリケーションを実行している、しかし私はそれが潜在的な競合状態を持っていると言われていると私はなぜわかりません。これは本当ですか?

+0

'-race'オプションでコードをビルドして実行しましたか? –

+0

@GrzegorzŻurはい両方のテストをビルドし、それは正常に動作します。 – danbondd

+1

マップの周りに同期がなく、同時に変更と読み取りを行うことができます。 – JimB

答えて

2

コードでは、読み込みではなく書き込みのみを同期しています。それはあなたがまだ書いている間に地図から読むことができることを意味します。これは競争状態になります。

スレッドセーフであるようにするには、チャネルまたはミューテックスを使用して同時実行構造で読み取りと書き込みの両方をラップする必要があります。書き込み動作。スレッドが書き込まれていない限り、複数のスレッドを同時に安全に読み取ることができます。 RWMutexは、この機能を明示的に提供します。

+0

右。それは私が克服しようとしていたものです。どのように不明なのですか? – Adrian

+0

"あなたはあなたの書き込みではなく、あなたの書き込みだけを同期しています。つまり、書き込み中に地図から読み込むことができます。質問内のコードの正確な記述、および問題の説明です。 – Adrian

+1

いいえ、提案は「スレッドセーフなものにするために」続きます。わかりやすくするために編集します。 – Adrian

関連する問題