-
Notifications
You must be signed in to change notification settings - Fork 17.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
sync: mutex.TryLock #6123
Comments
In the (I hope unlikely) event we decide to add this, please don't call it TryLock, a poor name for a boolean-valued function. (You tried, yes, but did you get it?) Maybe CanLock or GetLock; there's probably a better name I'm not thinking of. Arguments that Java calls it tryLock butter none of my parsnips. Labels changed: added priority-someday, removed priority-triage, go1.2maybe. |
"TryLock" or "GetLock" is fine. I don't care what you call it. "CanLock" says "Can I Lock" not "Get a Lock if you can". Java, Ruby, C++ (std::), and Objective-C have a TryLock function (and call it that), just to name a few. Python has a single `Acquire(block bool) bool` function. Why do you not want this? The sync.Mutex package is greatly limited compared to other languages. Just because Go has channels doesn't mean the sync.Mutex package should be left incomplete. Developers should be able to choose which to use: channel or mutex. They shouldn't be forced to use channels or fend for themselves. |
Some problems yes, but not all of them. Many of the issues I've had where channels aren't appropriate are buffers. I'm requesting this in behalf of others, so they would have to comment on their specific needs. To give a more specific example in my case, LockBefore(Time) function would be useful in the case of: http://golang.org/src/pkg/io/pipe.go http://golang.org/src/pkg/net/pipe.go In implementing SetReadDeadline and SetWriteDeadline. I believe that sync.Mutex should be more in parity with other languages; Just because a channel can do something doesn't mean sync.Mutex should be left incomplete. I would rather be given the choice rather than no choice at all. TryLock/GetLock/Acquire (or whatever you call it) should be easy to implement. So there is really no reason why it shouldn't be. LockBefore(Time) should be implemented to, but I understand that introducing a timed delay is more complex with the scheduler. |
Thanks for the response, and I can see where you're coming from. But: 1. My question was directly about TryLock, and you responded with arguments for LockBefore. 2. Every feature is useful. If it wasn't useful, we wouldn't be talking about it. Go is respected both for what it provides, and also for what it leaves out. It doesn't matter if the feature is easy to implement. Often "it is easy to do" is a very bad reason to add features. This is not me arguing against TryLock. I have no strong opinion one way or another. Rather, I am trying to find the argument *for* TryLock. |
There's an ongoing topic on the mailing list about it. I posted a link, so hopefully others can give you a more concrete examples. There have also been other topics about it in the past, and I'm sure it will come up again and again. The only example I can think of off the top of my head: When protecting a value, like a buffer, that you would otherwise use a sync.Mutex/sync.RWMutex, and you wanted to have a blocking (uses Lock()) and non-blocking call (uses TryLock()). A situation where you'd end up doing something like this: lock := make(chan bool) ... select { case <-lock: // do something default: return errors.New("Busy") } When this would have been much more concise: if m.TryLock() == false { return errors.New("Busy") } // do something Channels are also harder in this situation when you need a sync.RWMutex. |
IMHO TryLock (let me stick to the OP's proposed name to not add confusion) is a reasonable thing to have. But only in a classical, old school world of threads. Threads are expensive. Both to create and to switch context to. Whenever there's an option to blocking such expensive thing and do whatever useful instead, then it buys better resource allocation - hopefully. However, this is sort of a manual scheduling. Or at least scheduling "hint". In the "new" world composed of goroutines the above doesn't hold anymore. Goroutines are cheap. Both to create and to switch context to. When a goroutine gets blocked, no drama happens. Typically there are other goroutines in the ready state available for scheduling. If not, the process sleeps, but that's what good processes are expected to do when idle b/c of blocked on I/O, timer, ... Also, "explicit" scheduling by blocking on mutexes or channel operations is more easily reasoned about than is the case of a goroutine which tries to get a lock, in which case it does Foo, but if that fails it performs Bar. Conclusion: I think that adding TryLock encourages programming practices obsolete from the Go execution model POV (like eg. busy waiting). Make your goroutines simple; doing one thing and doing it well. It'll save you headaches while bug hunting. -1 from me. |
We need very strong use cases to add this. If they would exist, we've most likely noticed them already. As 0xjnml said, pthread/C/Java primitives are designed for very different environment and programming model. I can imagine a rare case for only trylocked mutex along the lines of: for ... { // do something local if atomic.SwapUint32(&merger, 1) == 0 { // merge local results into global results atomic.StoreUint32(&merger, 0) } } But this is easily solved with atomic operations. As for TimedLock, mutex is the very wrong level to solve priorities/deadlines/timeouts. #WontFix |
Most recent mailing list discussion: https://groups.google.com/d/topic/golang-nuts/vfbEGJCHGXM/discussion |
It's been more than a year since the last comment on this issue, and nobody has provided a compelling argument for this feature. I don't think it's going to happen. |
In case someone else comes along here, I think this would work for a general-purpose non-blocking semaphore:
It's not as efficient as it could be, but it's very simple and easily understandable as it uses basic language features (and is flexible enough to allow any type in the channel). |
How about creating a pool of resources (say connections or buffers) ? Code would look like this:
This is like sync.Pool but for resources that you don't want deallocated when free.
The good side of using channels is that the "WaitGroup logic" is straightforward. Also, you dont need to iterate over the busy resources. The only disadvantage with using channels that I can see is that freeing a resource is locking if you reach channel capacity. But then again, you can always call freeResource in a goroutine if you can't estimate the buffering you are going to need. Although this would seem weird, considering you have MAX_POOL_SIZE, think of a case when MAX_POOL_SIZE is a soft limit, that is, you have two different calls for adquiring resources; Would this pattern be correct? |
If you are asking a question, please ask on the mailing list [email protected]. Please don't ask questions on the issue tracker. And particularly please don't comment on closed issues; if you want to reopen one, bring that up on the mailing list as well. Thanks. |
The text was updated successfully, but these errors were encountered: