如何在goroutine闭包中更改外部变量的值

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

How to change external variable's value inside a goroutine closure

问题

如何在 goroutine 闭包内更改 ctx.Response.Status 的值?

要在 goroutine 闭包内更改 ctx.Response.Status 的值,可以使用指针来传递 ctx 对象,并在闭包内使用该指针进行修改。以下是修改的示例代码:

func (this *l) PostUpload(ctx *Context) {
    //ctx.Response.Status = 500

    l, err := models.NewL(this.Config)
    go func(ctx *Context) {
        err = l.Save(file) 
        if err != nil {
            ctx.Response.Status = 500
            ctx.Response.Body = err
        } else {
            ctx.Response.Status = 204
        }
    }(ctx)
}

在闭包内,我们将 ctx 对象作为参数传递给匿名函数,并在函数内部使用该参数进行修改。这样就可以在 goroutine 闭包内更改 ctx.Response.Status 的值。

英文:
func (this *l) PostUpload(ctx *Context) {

  	//ctx.Response.Status = 500

    l, err := models.NewL(this.Config)
    go func() {
	    err = l.Save(file) 
	    if err != nil {
		    ctx.Response.Status = 500
		    ctx.Response.Body = err
	    } else {
		    ctx.Response.Status = 204
	    }
    }()
}

How to change ctx.Response.Status value inside the goroutine closure?

答案1

得分: 3

您无法保证在另一个goroutine中对变量值的更改进行观察,除非进行同步。有关详细信息,请参阅Go内存模型

因此,如果您想在另一个goroutine中更改ctx.Response.Status,并确保这个更改在调用goroutine中可见,您需要进行同步。

有多种同步原语可供使用。您可以使用通道或sync包。

使用通道:

ch := make(chan int)

go func() {
    err = l.Save(file) 
    if err != nil {
        ctx.Response.Status = 500
        ctx.Response.Body = err
    } else {
        ctx.Response.Status = 204
    }
    ch <- 0 // 信号表示ctx已更新
    // goroutine可以执行其他工作(与更改ctx无关)
}()

<- ch // 等待goroutine完成更新ctx

使用sync.WaitGroup

var wg sync.WaitGroup
wg.Add(1)

go func() {
    err = l.Save(file) 
    if err != nil {
        ctx.Response.Status = 500
        ctx.Response.Body = err
    } else {
        ctx.Response.Status = 204
    }
    wg.Done() // 信号表示ctx已更新
    // goroutine可以执行其他工作(与更改ctx无关)
}()

wg.Wait() // 等待goroutine完成更新ctx

使用sync.Mutex

m := sync.Mutex{}
m.Lock()

go func() {
    err = l.Save(file) 
    if err != nil {
        ctx.Response.Status = 500
        ctx.Response.Body = err
    } else {
        ctx.Response.Status = 204
    }
    m.Unlock() // 信号表示ctx已更新
    // goroutine可以执行其他工作(与更改ctx无关)
}()

m.Lock() // 等待goroutine完成更新ctx

注意:

使用defer来发出完成信号(在您的情况下是ctx更新)是一种良好的做法,这样,如果启动的goroutine以某种意外的方式结束(例如运行时恐慌),调用goroutine将不会永远被阻塞。请注意,然而,在这种情况下,完成信号只会在匿名函数的末尾发送(这是延迟函数执行的时机)。

英文:

You have no guarantee to observe changes made to the value of a variable in another goroutine without synchronization. See The Go Memory Model for details.

So if you want to change ctx.Response.Status in another goroutine, for this change to be guaranteed to be visible in the caller goroutine use synchronization.

There are multiple synchronization primitives. You can use channels or the sync package.

Using channels:

ch := make(chan int)

go func() {
    err = l.Save(file) 
    if err != nil {
        ctx.Response.Status = 500
        ctx.Response.Body = err
    } else {
        ctx.Response.Status = 204
    }
    ch &lt;- 0 // Signal that ctx is updated
    // goroutine may do other works (not related to changing ctx)
}()

&lt;- ch // Wait for the goroutine to finish updating ctx

Using sync.WaitGroup:

var wg sync.WaitGroup
wg.Add(1)

go func() {
    err = l.Save(file) 
    if err != nil {
        ctx.Response.Status = 500
        ctx.Response.Body = err
    } else {
        ctx.Response.Status = 204
    }
    wg.Done() // Signal that ctx is updated
    // goroutine may do other works (not related to changing ctx)
}()

wg.Wait() // Wait for the goroutine to finish updating ctx

Using sync.Mutex:

m := sync.Mutex{}
m.Lock()

go func() {
    err = l.Save(file) 
    if err != nil {
        ctx.Response.Status = 500
        ctx.Response.Body = err
    } else {
        ctx.Response.Status = 204
    }
    m.Unlock() // Signal that ctx is updated
    // goroutine may do other works (not related to changing ctx)
}()

m.Lock() // Wait for the goroutine to finish updating ctx

Note:

It is good practice to signal the completion (ctx update in your case) using defer so that if the started goroutine would end in some unexpected way (e.g. runtime panic), the caller goroutine would not get blocked forever. Note that however in this case the completion signal will only be sent at the end of the anonymous function (that's when deferred functions are executed).

huangapple
  • 本文由 发表于 2015年8月13日 16:36:14
  • 转载请务必保留本文链接:https://go.coder-hub.com/31983275.html
匿名

发表评论

匿名网友

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

确定