2017-04-17 15 views
3

我有一个非常基本的Golang应用程序,它可以创建并运行HTTP服务器。服务器有2个端点,一个发送数据和一个接收。同时从地图上书写和读取会导致潜在的竞争条件

在每次POST请求发送到服务器,解析从主体输入的数据并将其推入的信道。然后我有一个函数可以读取通道并将数据保存到地图中。

在每个GET请求到服务器,JSON编组地图并将其发送到客户端。

每个请求的HTTP服务器异步运行,工人功能在其自身的goroutine同步写入地图。

基本伪代码如下:

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