如何使用Go http包提供共享结构?

时间:2017-08-25 09:05:37

标签: http go concurrency go-templates

我有一个包含许多字段的结构(其中一些是指向其他结构的指针),它们在一个单独的goroutine中连续更新。在提供页面时,可以从go http模板访问相同的结构。

代码示例:

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)
}

page.html模板:

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

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

1 个答案:

答案 0 :(得分: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)
}

// Code that modifies 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字段的一小部分,那么当然只能制作那些字段的副本就足够了。