How to serve shared struct with Go http package?

huangapple go评论141阅读模式
英文:

How to serve shared struct with Go http package?

问题

我有一个包含许多字段的结构体(其中一些字段也是指向其他结构体的指针),这些字段在一个单独的goroutine中不断更新。当页面被服务时,相同的结构体从Go的http模板中访问。

代码示例:

type SharedStruct struct {
    Description string
    Counter int
    Status_ *Status
    LastChecked time.Time
    //其他字段
} 
var shared = &SharedStruct{}

go func() {
    //..每5分钟更新字段
}()

func someHandler(w http.ResponseWriter, r *http.Request) {
    t.ExecuteTemplate(w, "page.html", shared)
}

page.html模板:

...
Status: {{.Status_.StatusCode}}
Counter: {{.Counter}}
Last checked: {{.LastChecked.Format "2006-02-01 15:04:05"}}

到目前为止,一切都按预期工作,但我知道在没有任何同步的情况下可能会发生糟糕的事情。如何正确处理这个问题的首选方法是什么?

英文:

I have a struct with many fields (some of them are pointers to other structs as well) which are being continuosly updated in a separate goroutine. The same struct is accessed from go's http template when a page is served.

Code example:

type SharedStruct struct {
     Description string
     Counter int
     Status_ *Status
     LastChecked time.Time
     //other fields
} 
var shared = &SharedStruct{}

go func() {
    //..updates fields every 5 minutes
}()

go-http handler:

func someHandler(w http.ResponseWriter, r *http.Request) {
   t.ExecuteTemplate(w, "page.html", shared)
}

and page.html template:

...
Status: {{.Status_.StatusCode}}
Counter: {{.Counter}}
Last checked: {{.LastChecked.Format "2006-02-01 15:04:05"}}

So far everything works as expected, but I'm aware that bad things can happen without any synchronization. What is the preferred way to handle this properly?

答案1

得分: 2

首选的方法与其他情况相同。

在读取/更新共享结构时,可以使用互斥锁:

var shared = &SharedStruct{}
var mux = &sync.RWMutex{}

func someHandler(w http.ResponseWriter, r *http.Request) {
    mux.RLock()
    defer mux.RUnlock()
    t.ExecuteTemplate(w, "page.html", shared)
}

// 修改 shared 的代码:
mux.Lock()
shared.Counter++
mux.Unlock()

或者,如果模板执行时间很长,复制shared结构并在执行模板时传递副本可能是有益的,这样在模板执行期间对shared的访问不会被阻塞。请注意,在进行复制时仍然需要使用互斥锁。如果不仅指针而且指向的值也可能发生更改,则还必须复制这些值:

func someHandler(w http.ResponseWriter, r *http.Request) {
    mux.RLock()
    shared2 := &SharedStruct{}
    *shared2 = *shared
    shared2.Status_ = new(Status)
    *shared2.Status_ = *shared.Status_
    mux.RUnlock()
    
    t.ExecuteTemplate(w, "page.html", shared2)
}

如果模板只使用shared字段的一个小子集,当然只需复制那些字段即可。

英文:

The preferred way is the same as in any other cases.

Either use a mutex when the shared struct is read / updated:

var shared = &SharedStruct{}
var mux = &sync.RWMutex{}

func someHandler(w http.ResponseWriter, r *http.Request) {
    mux.RLock()
    defer mux.RUnlock()
    t.ExecuteTemplate(w, "page.html", shared)
}

// Code that modifies shared:
mux.Lock()
shared.Counter++
mux.Unlock()

Or if the template execution takes long time, it may be protitable to make a copy of the shared struct and pass the copy when executing the template, so that during template execution access to shared is not blocked. Note that while making the copy you still have to use a mutex. Also if not only the pointers but the pointed values may change, you also have to make a copy of those:

func someHandler(w http.ResponseWriter, r *http.Request) {
    mux.RLock()
    shared2 := &SharedStruct{}
    *shared2 = *shared
    shared2.Status_ = new(Status)
    *shared2.Status_ = *shared.Status_
    mux.RUnlock()
    
    t.ExecuteTemplate(w, "page.html", shared2)
}

If the template only uses a small subset of the shared fields, it is enough to make only a copy of those of course.

huangapple
  • 本文由 发表于 2017年8月25日 17:05:36
  • 转载请务必保留本文链接:https://go.coder-hub.com/45877980.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定