Live data from Hacker News

Singleton Pattern in Go

marcio.io

11–20 of 76 posts

Re: Singleton Pattern in Go

#11
post #8
post #5

This seems more idiomatic: func init() { instance = &singleton{} }

If instance can truly be initialized like that, then there's no need for the init() function, just do it at the top level: var instance = &singleton{} In fact, a lot of idiomatic Go will ignore the Get() method, exporting the instance itself: var Instance = &singleton{} Clearly if you overwrite it, bad things will happen. Don't do that. There may possibly be a problem with unnecessarily slowing startup times in the c…

> There may possibly be a problem with unnecessarily slowing startup times in the case you don't need the singleton - in which case, profiling will tell you and you can revert to the method in the blog post.

Not just in that case. If you lazily initialize then you can get other things done before—or while—you're waiting for the singleton constructor to run. Global constructors are suboptimal for performance almost as a rule.

Re: Singleton Pattern in Go

#12

Wouldn't it make more sense to have some sort of manager that receives information on an incoming channel, manages the state internally, and reports out on an outgoing channel?

Abstractly, if you somehow need a singleton object, this will be substantially faster if you need it frequently. And if you're careful to do all the other things you need to do in order to make this work and safe, as seen in the other messages in this post.

Practically, all the examples I've ever of why you'd ever want a singleton are indeed better done as a goroutine server over channels. Within the object you get an implicit lock by virtue of being the only goroutine ever to touch that stuff, and as long as you don't need this object to use more than one CPU total and the overhead of channels isn't a big deal, which again, describes the vast bulk of cases I've ever heard of that call for singletons, it's a better way to go.

And I'd note I've written a couple dozen different goroutine server things and a grand total of 0 'singletons', so... yeah.

If I had an immutable singleton object that was somehow very expensive to initialize, I might consider this approach. Not sure when that would come up but I'm sure it describes something.

Re: Singleton Pattern in Go

#14
post #9

This post is chock-full of antipatterns. Both the singleton pattern and double checked locking should be avoided.

Yea it's easy enough to use dependency injection to satisfy these supposedly "global" requirements. About the only global state I can tolerate is read-only constants, but even then you are much better off with DI just because of testing.

Re: Singleton Pattern in Go

#15
post #6

I thought there was a lot of literature pretty convincingly arguing that singleton is in fact an anti-pattern.

There is, singletons are an anti-pattern, they're still interesting to discuss like this in terms of making them safe. In a language with both concurrency primitives and globals singletons have little (no) place anyway. I still learned from the post.

Re: Singleton Pattern in Go

#16
post #12

Wouldn't it make more sense to have some sort of manager that receives information on an incoming channel, manages the state internally, and reports out on an outgoing channel?

Abstractly, if you somehow need a singleton object, this will be substantially faster if you need it frequently. And if you're careful to do all the other things you need to do in order to make this work and safe, as seen in the other messages in this post. Practically, all the examples I've ever of why you'd ever want a singleton are indeed better done as a goroutine server over channels. Within the object you get a…

A singleton is basically a global variable and those aren't really important to "shim" in Go. Object method calls are pretty much the same conceptually as message passing but when you actually need to synchronize as you point out goroutines and channels make things much simpler. I think what this post was going for wasn't "use singletons", it was "look at these interesting threading issues that arise and how we address them in (non idiomatic) Go"

Re: Singleton Pattern in Go

#17
post #16
post #12

Earlier quoted context omitted.

Abstractly, if you somehow need a singleton object, this will be substantially faster if you need it frequently. And if you're careful to do all the other things you need to do in order to make this work and safe, as seen in the other messages in this post. Practically, all the examples I've ever of why you'd ever want a singleton are indeed better done as a goroutine server over channels. Within the object you get a…

A singleton is basically a global variable and those aren't really important to "shim" in Go. Object method calls are pretty much the same conceptually as message passing but when you actually need to synchronize as you point out goroutines and channels make things much simpler. I think what this post was going for wasn't "use singletons", it was "look at these interesting threading issues that arise and how we addre…

There's more that needs to be done if the goal is "look at these interesting threading issues that arise and how we address them in (non idiomatic) Go", because the initialization described in the post is broken.

Shared-memory multithreading is hard. Golang does not do much to prevent you from shooting yourself in the foot here, as this post proves. So use the abstractions wherever possible. In this case, Once correctly performs the atomics, and naive double-checked locking doesn't.

Re: Singleton Pattern in Go

#18
post #4

Your "check-lock-check" code is probably broken (depending on the intricacies of Golang's memory model). If the compiler or CPU reorders any stores to the fields of "instance" after the assignment to "instance" itself, other threads could start working with a partially uninitialized object. Once() uses atomics on the fast path for a reason.

Totally! I was going to post the same thing. Double-checked locking is either impossible or really hard to get right, depending on the language and architecture's guarantees!

If you must use a singleton, I'd really recommend doing the so-called "aggressive" approach, which should have really been named the "actually won't crash sometimes" approach.

Re: Singleton Pattern in Go

#19
post #3

This is cute, but I think (someone will correct me if I'm wrong) that the real idiom is: things that would be singletons in Java are instead servers managing a channel in Golang.

Why incur the cost and complexity of a channel if what you are doing doesn't require it? Go's sync.Once just implements the check-lock-check pattern. It doesn't use a channel in the background.

In fact, it is better to use sync.Once to reduce the risk getting the semantics of check-lock-check incorrect. :)

[edit: It occurred to me that the parent may be speaking about architecture and not about the singleton's design. From an architectural perspective, there are much better patterns in golang than a singleton.]

Re: Singleton Pattern in Go

#20
post #19
post #3

This is cute, but I think (someone will correct me if I'm wrong) that the real idiom is: things that would be singletons in Java are instead servers managing a channel in Golang.

Why incur the cost and complexity of a channel if what you are doing doesn't require it? Go's sync.Once just implements the check-lock-check pattern. It doesn't use a channel in the background. In fact, it is better to use sync.Once to reduce the risk getting the semantics of check-lock-check incorrect. :) [edit: It occurred to me that the parent may be speaking about architecture and not about the singleton's design…

The architecture! Not the design of this singleton.
Post reply on HN