Home  >  Article  >  Backend Development  >  Concurrency-safe templates in Go: How do I do it?

Concurrency-safe templates in Go: How do I do it?

WBOY
WBOYforward
2024-02-08 21:50:10911browse

Go 中的并发安全模板:我该怎么做?

Concurrency safety templates in Go are a key issue. For programs that need to operate in a concurrent environment, ensuring the security of data is crucial. When dealing with concurrency, we need to take some measures to protect shared resources and avoid race conditions and data races. In this article, I will introduce you to some commonly used concurrency safety templates, help you understand the concept of concurrency safety, and provide some practical suggestions. Both beginners and experienced developers can benefit from it. Let’s explore how to achieve concurrency safety in Go!

Question content

I have the following phone number:

import (
  "text/template"
)

//...

template.new(filepath.base(name)).funcs(templatefunctions).parse(string(asset))

Called simultaneously in multiple go routines, This in turn leads to the following panic:

fatal error: concurrent map iteration and map write

This is the traceback:

goroutine 140 [running]:
text/template.addvaluefuncs(0xc00188e000?, 0xc00188e000?)
        [...]/go/src/text/template/funcs.go:88 +0x76
[...]/modules/template.loadembeddedtemplates({0x38ff6cb?, 0xc001cf8060?})
        [...]/src/modules/template/configbased.go:163 +0x749
The line at

src/modules/template/configbased.go:163 quoted above. It is template.new(...).

The surrounding functions are called simultaneously from goroutine.

Here is the code from go/src/text/template/funcs.go:88 If it helps:

// addvaluefuncs adds to values the functions in funcs, converting them to reflect.values.
func addvaluefuncs(out map[string]reflect.value, in funcmap) {
    for name, fn := range in {
        if !goodname(name) {
            panic(fmt.errorf("function name %q is not a valid identifier", name))
        }
        v := reflect.valueof(fn)
        if v.kind() != reflect.func {
            panic("value for " + name + " not a function")
        }
        if !goodfunc(v.type()) {
            panic(fmt.errorf("can't install method/function %q with %d results", name, v.type().numout()))
        }
        out[name] = v
    }
}

If template.new is concurrency safe, why does this line generate this panic and how should I handle it correctly?

renew.

Code of

annoying functionloadembeddedtemplates:

func loadEmbeddedTemplates(templateFile string) (*template.Template, error) {
    var t *template.Template

    templateFile = filepath.Join("share", "templates", filepath.Base(templateFile))
    dir := filepath.Dir(templateFile)
    names := assets.GetAssetNames()

    // All templates except + the last one at the end
    filteredNames := []string{}

    for _, name := range names {
        if !strings.HasPrefix(name, dir+"/") || !strings.HasSuffix(name, ".tmpl") {
            continue
        }

        if name != templateFile {
            filteredNames = append(filteredNames, name)
        }
    }

    filteredNames = append(filteredNames, templateFile)

    for _, name := range filteredNames {
        asset, err := assets.GetAsset(name)
        if err != nil {
            return nil, err
        }

        if t == nil {
            t, err = template.New(filepath.Base(name)).Funcs(templateFunctions).Parse(string(asset))
        } else {
            t, err = t.New(filepath.Base(name)).Parse(string(asset))
        }

        if err != nil {
            return nil, err
        }
    }

    return t, nil
}

This function just loads all templates in share/templates/ in sequence

Solution

Your loadEmbeddedTemplates() Function accesstemplateFunctions variable, pass it to Template.Funcs() which will obviously read it (will iterate over it).

And you might populate it in another goroutine at the same time. Hence the concurrent map write error. Access to it must be synchronized.

If possible, populate it before starting to use it (pass it to Template.Funcs()). This way no additional synchronization or locking is required (concurrent read-only is always possible).

The above is the detailed content of Concurrency-safe templates in Go: How do I do it?. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:stackoverflow.com. If there is any infringement, please contact admin@php.cn delete