Go语言全局变量避免被协程覆盖

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

Go lang global variables without goroutines overwriting

问题

我正在用Go语言编写一个CMS,并且有一个会话类型(用户ID,要渲染的页面内容等)。理想情况下,我希望这个类型是一个全局变量,这样我就不需要在所有嵌套函数中传递它,但是拥有这样一个全局变量显然意味着每个新的会话都会覆盖它的前一个会话,这无疑是一个失败。

有些语言提供了在线程内保留全局变量的方法(即该全局变量的值在该线程内被隔离)。虽然我知道Goroutines不是线程,但我想知道是否有类似的方法可供我使用,或者我是否需要通过各种嵌套的例程传递我的会话类型的本地指针。

我猜通道不会做到这一点?从我所了解的(如果我理解错了,请纠正我),它们基本上只是一种安全共享全局变量的方式?

编辑:我忘记了这个问题!无论如何,对于任何感兴趣的人,这是一个更新。这个问题是在我刚开始使用Go语言并且CMS基本上是我的第一个项目时写的。我以前是从C背景过来的,熟悉POSIX线程,但我很快意识到更好的方法是以一种更模块化的设计方式编写代码,并将会话对象作为指针传递给函数参数。这既给我带来了我想要的上下文敏感的局部作用域,同时也最小化了我复制的数据量。然而,作为一个七年的项目,并且是我转向Go的开始,可以说该项目无论如何都需要进行重大重写,因为有很多错误。不过,这是另一天的问题 - 目前它可以工作,并且我有足够多的其他项目在进行中。

英文:

I'm writing a CMS in Go and have a session type (user id, page contents to render, etc). Ideally I'd like that type to be a global variable so I'm not having to propagate it through all the nested functions, however having a global variable like that would obviously mean that each new session would overwrite it's predecessor, which, needlessly to say, would be an epic fail.

Some languages to offer a way of having globals within threads that are preserved within that thread (ie the value of that global is sandboxed within that thread). While I'm aware that Goroutines are not threading, I just wondered if there was a similar method at my disposal or if I'd have to pass a local pointer of my session type down through the varies nested routines.

I'm guessing channels wouldn't do this? From what I can gather (and please correct me if I'm wrong here), but they're basically just a safe way of sharing global variables?

edit: I'd forgotten about this question! Anyhow, an update for anyone who is curious. This question was written back when I was new to Go and the CMS was basically my first project. I was coming from a C background with familiarity with POSIX thread but I quickly realised a better approach was to write the code in a mode functional design with session objects passed down as pointers in function parameters. This gave me both the context-sensitive local scope I was after while also minimizing the amount to data I was copying about. However being a 7 year old project and one that was at the start of my transition to Go, it's fair to say the project could do with a major rewrite anyway as there are a lot of mistakes made. That's a concern for another day though - currently it works and I have enough other projects on the go at.

答案1

得分: 4

你需要使用类似于Context的东西:

http://blog.golang.org/context

基本上,模式是为每个想要做的独特的事情创建一个Context(在你的情况下是一个web请求)。使用context.WithValue将多个变量嵌入到上下文中。然后,始终将其作为其他在其他goroutine中进行进一步工作的方法的第一个参数传递。

从上下文中获取所需的变量只需在任何goroutine中调用context.Value。来自上述链接的内容:

> Context可以同时被多个goroutine安全使用。代码可以将单个Context传递给任意数量的goroutine,并取消该Context以通知它们所有。

我曾经有一个实现,我明确地将变量作为方法参数发送,然后我发现使用上下文来嵌入这些变量显著简化了我的代码。

使用Context还有助于通过使用通道、select和一个名为“done channel”的概念来结束长时间运行的任务。阅读这篇文章以了解一个很好的基本回顾和实现:

http://blog.golang.org/pipelines

我建议先阅读pipelines文章,以了解如何管理goroutine之间的通信,然后再阅读上下文文章,以更好地了解如何提升并开始嵌入变量以进行传递。

祝你好运!

英文:

You'll want to use something like a Context:

http://blog.golang.org/context

Basically, the pattern is to create a Context for each unique thing you want to do. (A web request in your case.) Use context.WithValue to embed multiple variables in the context. Then always pass it as the first parameter to other methods that are doing further work in other goroutines.

Getting the variable you need out of the context is a matter of calling context.Value from within any goroutine. From the above link:

> A Context is safe for simultaneous use by multiple goroutines. Code can pass a single Context to any number of goroutines and cancel that Context to signal all of them.

I had an implementation where I was explicitly sending variables as method parameters, and I discovered that embedding these variables using contexts significantly cleaned up my code.

Using a Context also helps because it provides ways to end long-running tasks by using channels, select, and a concept called a "done channel." See this article for a great basic review and implementation:

http://blog.golang.org/pipelines

I'd recommend reading the pipelines article first for a good flavor of how to manage communication among goroutines, then the context article for a better idea of how to level-up and start embedding variables to pass around.

Good luck!

答案2

得分: 1

不要使用全局变量。使用Go goroutine本地变量。

已经有了goroutine本地变量:它们被称为函数参数、函数返回值和局部变量。

Russ

英文:

Don't use global variables. Use Go goroutine-local variables.

> go-routine Id..
>
> There are already goroutine-local variables: they are called function
> arguments, function return values, and local variables.
>
> Russ

答案3

得分: 1

如果您有多个用户,那么您不需要为每个连接获取该信息吗?所以我认为您会为每个连接的用户拥有一个结构体。在设置工作协程时,将该结构体的指针传递给它是Go语言的惯用方式,或者通过通道传递指针。

英文:

If you have more than one user, then wouldn't you need that info for each connection? So I would think that you'd have a struct per connected user. It would be idiomatic Go to pass a pointer to that struct when setting up the worker goroutine, or passing the pointer over a channel.

huangapple
  • 本文由 发表于 2013年4月3日 00:10:46
  • 转载请务必保留本文链接:https://go.coder-hub.com/15768970.html
匿名

发表评论

匿名网友

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

确定