英文:
Golang map update on http handlefunc
问题
我对Go语言中的map有一个问题。
我想要处理客户端(http
)并使用map
(键(客户端IP)值对)保存一些他们的信息...
http
使用一个新线程处理每个http客户端,所以我认为改变(添加、删除、编辑)map
数据将是不安全的...我的操作安全吗?
package main
import (
"net/http"
)
func main() {
var clientsData map[string]string
http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
// 这个map的添加操作是安全的吗?还是我需要使用线程锁(互斥锁等)?
clientsData[request.RemoteAddr] = ...
})
http.ListenAndServe("127.0.0.10:8090", nil)
}
英文:
I have a question about map in go language.
I want to handle the clients (http
) and save some of their information using map
(key (client IP) value pair) ...
http
handle each http client using a new thread, so I think changing (add, delete, edit) the map
data will be unsafe ... Is my action safe ?
package main
import (
"net/http"
)
func main() {
var clientsData map[string]string
http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
// Is this map adding, safe or i have to use thread lock (mutex or ...) ?
clientsData[request.RemoteAddr] = ...
})
http.ListenAndServe("127.0.0.10:8090", nil)
}
答案1
得分: 2
简化示例中的代码,可以构建一个简单的示例来展示它是不安全的。
使用以下简单程序:
package main
import (
"net/http"
)
func main() {
counters := map[string]int{}
name := "test"
counters[name] = 0
http.HandleFunc("/test", func(w http.ResponseWriter, req *http.Request) {
counters[name]++
})
http.ListenAndServe(":8000", nil)
}
并使用以下命令进行模拟:
ab -n 20000 -c 200 "127.0.0.1:8000/test"
会产生如下异常:
goroutine 158 [running]:
runtime.throw({0x64d95a, 0x81faa0})
/usr/local/go/src/runtime/panic.go:1198 +0x71 fp=0xc000384980 sp=0xc000384950 pc=0x4348f1
runtime.mapaccess2_faststr(0x697360, 0xc0003a4ba0, {0x644851, 0x4})
/usr/local/go/src/runtime/map_faststr.go:116 +0x3d4 fp=0xc0003849e8 sp=0xc000384980 pc=0x413d34
main.main.func1({0x69bf00, 0xc0003a4b60}, 0x0)
/home/test/gohttp/main.go:13 +0x46 fp=0xc000384a48 sp=0xc0003849e8 pc=0x5eba86
net/http.HandlerFunc.ServeHTTP(0x0, {0x69bf00, 0xc0003a4b60}, 0x0)
这个示例展示了并发访问时的不安全性。
英文:
Simplying the sample in https://eli.thegreenplace.net/2019/on-concurrency-in-go-http-servers/ allow to build a simple example that show it is not safe.
Using a simple program like :
package main
import (
"net/http"
)
func main() {
counters := map[string]int{}
name := "test"
counters[name] = 0
http.HandleFunc("/test", func(w http.ResponseWriter, req *http.Request) {
counters[name]++
})
http.ListenAndServe(":8000", nil)
}
And stimulating using :
ab -n 20000 -c 200 "127.0.0.1:8000/test"
Produce exception like :
goroutine 158 [running]:
runtime.throw({0x64d95a, 0x81faa0})
/usr/local/go/src/runtime/panic.go:1198 +0x71 fp=0xc000384980 sp=0xc000384950 pc=0x4348f1
runtime.mapaccess2_faststr(0x697360, 0xc0003a4ba0, {0x644851, 0x4})
/usr/local/go/src/runtime/map_faststr.go:116 +0x3d4 fp=0xc0003849e8 sp=0xc000384980 pc=0x413d34
main.main.func1({0x69bf00, 0xc0003a4b60}, 0x0)
/home/test/gohttp/main.go:13 +0x46 fp=0xc000384a48 sp=0xc0003849e8 pc=0x5eba86
net/http.HandlerFunc.ServeHTTP(0x0, {0x69bf00, 0xc0003a4b60}, 0x0)
答案2
得分: 0
如果只是读取,是安全的。
如果需要写入,需要一种“线程安全”的方式来进行。
第一个想法是使用sync.Mutex来保护对映射的访问。
然而,这可能成为一个瓶颈,因为您可能有多个并行的请求,但每次只能写入一个。我们在谈论纳秒级的时间...
第二种方法是使用读/写互斥锁来控制读取和写入。许多goroutine可以读取,但每次只能写入一个。
还有其他来自sync和sync/atomic包的选项。
还有一种额外的方法要考虑:如果您只需要写入此映射,可以考虑使用带缓冲的通道将键/值结构发送到单个goroutine中(负责将其存储到映射中)。
正如您所看到的,这种方法有许多优点,如果对您的应用程序有意义的话。
您甚至可以使用通道通过回调函数进行安全读取/写入,但这是另外一个故事。
如果您有疑问,请编写单元测试并使用竞争条件检测器。
英文:
If you only read, it is safe.
If you need to write, you need some way to do it “thread-safe”
The first idea is to use a sync.Mutex to protect the access to the map
However this may became a bottleneck, since you may have several requests in parallel but only one can write each time. We are talking about nanoseconds…
A second approach can be use a read/write mutex to control reading and writting. Many goroutines can read, but only one can write per time.
There are other options from package sync and sync/atomic.
There is one extra approach to consider: if you need only write into this map you can consider use a buffered channel to send a structure of key/value to be consumed in a single goroutine (responsible for store it into the map)
As you can see, this approach have many
Advantages IF it makes sense to your application.
You can even use channels to safe read / write via callbacks but this is another story.
If you have a doubt, write unit tests and use the race condition detector
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论