英文:
Concurrently writing and reading from map causes potential race condition
问题
我有一个非常基本的Golang应用程序,它创建并运行一个HTTP服务器。该服务器有两个端点,一个用于发送数据,一个用于接收数据。
每次向服务器发送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
// 将数据推送到通道
stream <- data
}
}
func serveHandler(store *dataStore) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// 将数据存储转换为JSON
// 发送给客户端
}
}
func worker(stream <-chan string) *dataStore {
store := make(dataStore)
go func() {
for data := range stream {
// 处理并将数据写入映射
}
}()
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)
}
我已经测试并运行了该应用程序,没有出现任何问题,但有人告诉我它可能存在竞态条件,我不确定为什么。这是真的吗?
英文:
I have a very basic Golang application that creates and runs a HTTP server. The server has 2 endpoints, one to send data and one to receive.
Upon each POST request to the server, parse the incoming data from the body and push it onto a channel. I then have a function that reads from the channel and saves the data to a map.
Upon each GET request to the server, JSON marshal the map and send it to the client.
Each request to the HTTP server runs asynchronously and the worker function writes to the map synchronously in its own goroutine.
Basic pseudo code is as follows:
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)
}
I have tested and ran the application with no problems, however I have been told it has a potential race condition and I am not sure why. Is this true?
答案1
得分: 2
在给定的代码中,你只同步了写操作,而没有同步读操作;这意味着在写入映射时仍然可能从映射中读取数据,这将导致竞态条件。
为了使其线程安全,你需要在并发结构中包装读操作和写操作,可以使用通道或互斥锁,以确保在任何给定时间只有一个线程在访问它(如果是写操作)。多个线程可以同时安全地进行读取,只要没有线程在写入。RWMutex
提供了这种功能。
英文:
In the code given, you're only synchronizing your writes, not your reads; that means you still might read from the map while you're writing to it, which would be a race condition.
To make it thread-safe, you need to wrap both reads and writes in a concurrency structure - either using channels or a mutex - in order to ensure that only one thread is accessing it at any given time if it is a write operation. Multiple threads can safely read at the same time, as long as no thread is writing to it. RWMutex
provides this functionality explicitly.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论