如何以编程方式证明此代码具有竞争条件? [英] How to programatically prove this code has a race condition?

查看:85
本文介绍了如何以编程方式证明此代码具有竞争条件?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我被告知此代码在设计上具有竞争条件,尽管我尝试了一下,但我无法证明它确实存在.

I'm told this code has a race condition by design, though try as I may, I'm unable to prove it does.

func (h *handler) loggingMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        h.Log = log.WithFields(log.Fields{
            "method":     r.Method,
            "requestURI": r.RequestURI,
        })
        next.ServeHTTP(w, r)
    })
}

我尝试了go build -race,然后运行二进制文件:PORT=3000 ./main&加载hey -n 10000 -c 200 http://localhost:3000之类的创建者.

I tried go build -race, then running the binary: PORT=3000 ./main & load creators like hey -n 10000 -c 200 http://localhost:3000.

此处的其余代码: https://raw .githubusercontent.com/kaihendry/context-youtube/master/5/main.go

type handler struct{ Log *log.Entry }

func New() (h *handler) { return &handler{Log: log.WithFields(log.Fields{"test": "FAIL"})} }

func (h *handler) index(w http.ResponseWriter, r *http.Request) {
    h.Log.Info("index")
    fmt.Fprintf(w, "hello")
}

func (h *handler) about(w http.ResponseWriter, r *http.Request) {
    h.Log.Info("about")
    fmt.Fprintf(w, "about")
}

func main() {
    h := New()
    app := mux.NewRouter()
    app.HandleFunc("/", h.index)
    app.HandleFunc("/about", h.about)
    app.Use(h.loggingMiddleware)
    if err := http.ListenAndServe(":"+os.Getenv("PORT"), app); err != nil {
        log.WithError(err).Fatal("error listening")
    }
}

如果我不能证明它具有竞赛条件,我可以假定设置h.Log是安全的吗?

If I can't prove it has a race condition, can I assume setting h.Log is safe?

推荐答案

有一种编程方式,为此您必须做两件事:

There is a programmatic way, for which you have to do 2 things:

  • 再现民主状况
  • 并在启动go工具时使用-race选项
  • reproduce the racy condition
  • and use the -race option when launching the go tool

最好是为它编写一个单元测试,因此该测试也是可重复的,并且可以在每次构建/部署时自动运行/检查.

Best is if you write a unit test for it, so the test is also reproducible, and run / checked automatically on each build / deploy.

好,那么如何重现呢?

只需编写一个测试,该测试将有意地在没有同步的情况下启动两个goroutine,一个调用index处理程序,一个调用about处理程序,这就是触发竞赛检测器的原因.

Simply write a test which launches 2 goroutines, one which calls the index handler, and one which calls the about handler, deliberately without synchronization, this is what triggers the race detector.

使用 net/http/httptest 包可以轻松测试处理程序. httptest.NewServer() 会为您准备就绪的服务器,并与处理程序武装"在一起你传递给它.

Use the net/http/httptest package to easily test handlers. httptest.NewServer() hands you a ready server, "armed" with the handler you pass to it.

这是一个简单的测试示例,它将触发竞争条件.将其放在main.go文件旁边的名为main_test.go的文件中:

Here's a simple test example that will trigger the race condition. Put it in a file named main_test.go, next to your main.go file:

package main

import (
    "fmt"
    "net/http"
    "net/http/httptest"
    "sync"
    "testing"

    "github.com/gorilla/mux"
)

func TestRace(t *testing.T) {
    h := New()
    app := mux.NewRouter()
    app.HandleFunc("/", h.index)
    app.HandleFunc("/about", h.about)
    app.Use(h.loggingMiddleware)

    server := httptest.NewServer(app)
    defer server.Close()

    wg := &sync.WaitGroup{}
    for _, path := range []string{"/", "/about"} {
        path := path
        wg.Add(1)
        go func() {
            defer wg.Done()
            req, err := http.NewRequest(http.MethodGet, server.URL+path, nil)
            fmt.Println(server.URL + path)
            if err != nil {
                panic(err)
            }
            res, err := http.DefaultClient.Do(req)
            if err != nil {
                panic(err)
            }
            defer res.Body.Close()
        }()
    }
    wg.Wait()
}

您必须使用它运行

go test -race

示例输出为:

http://127.0.0.1:33007/
http://127.0.0.1:33007/about
==================
WARNING: DATA RACE
Write at 0x00c000098030 by goroutine 17:
  play.(*handler).loggingMiddleware.func1()
      /home/icza/tmp/gows/src/play/main.go:16 +0x1ce
  net/http.HandlerFunc.ServeHTTP()
      /usr/local/go/src/net/http/server.go:1964 +0x51
  github.com/gorilla/mux.(*Router).ServeHTTP()
      /home/icza/tmp/gows/src/github.com/gorilla/mux/mux.go:212 +0x12e
  net/http.serverHandler.ServeHTTP()
      /usr/local/go/src/net/http/server.go:2741 +0xc4
  net/http.(*conn).serve()
      /usr/local/go/src/net/http/server.go:1847 +0x80a

Previous write at 0x00c000098030 by goroutine 16:
  play.(*handler).loggingMiddleware.func1()
      /home/icza/tmp/gows/src/play/main.go:16 +0x1ce
  net/http.HandlerFunc.ServeHTTP()
      /usr/local/go/src/net/http/server.go:1964 +0x51
  github.com/gorilla/mux.(*Router).ServeHTTP()
      /home/icza/tmp/gows/src/github.com/gorilla/mux/mux.go:212 +0x12e
  net/http.serverHandler.ServeHTTP()
      /usr/local/go/src/net/http/server.go:2741 +0xc4
  net/http.(*conn).serve()
      /usr/local/go/src/net/http/server.go:1847 +0x80a

Goroutine 17 (running) created at:
  net/http.(*Server).Serve()
      /usr/local/go/src/net/http/server.go:2851 +0x4c5
  net/http/httptest.(*Server).goServe.func1()
      /usr/local/go/src/net/http/httptest/server.go:280 +0xac

Goroutine 16 (running) created at:
  net/http.(*Server).Serve()
      /usr/local/go/src/net/http/server.go:2851 +0x4c5
  net/http/httptest.(*Server).goServe.func1()
      /usr/local/go/src/net/http/httptest/server.go:280 +0xac
==================
2019/01/06 14:58:50  info index                     method=GET requestURI=/
2019/01/06 14:58:50  info about                     method=GET requestURI=/about
--- FAIL: TestRace (0.00s)
    testing.go:771: race detected during execution of test
FAIL
exit status 1
FAIL    play    0.011s

测试失败,表明存在数据争用.

The test fails, showing that there are data races.

注释:

sync.WaitGroup的同步是等待2个启动的goroutine,而不是同步对处理程序记录器的访问(这会导致数据争用).这样一来,如果您解决了数据争用问题,则测试将正常运行并结束(等待2个已启动的test-goroutines完成).

The synchronization with the sync.WaitGroup is to wait for the 2 launched goroutines, it is not to synchronize access to the handler's logger (which causes the data race). This is so if you fix the data race, the test will run and end properly (waiting for the 2 launched test-goroutines to complete).

这篇关于如何以编程方式证明此代码具有竞争条件?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆