I’m just getting started with Golang and the net/rpc package. I’m trying to understand when you might use the asynchronous client.Go() call over the client.Call() method most examples online use. Would calling client.Call asynchronously via something like
go client.Call(...)
essentially be the same as using the client.Go call? I’ve seen this example online too (for example when calling multiple simultaneous RPCs).
As documented:
Go invokes the function asynchronously. It returns the Call structure representing the invocation. The done channel will signal when the call is complete by returning the same Call object. If done is nil, Go will allocate a new channel. If non-nil, done must be buffered or Go will deliberately crash.
What this means is that it issues the command, but does not wait for it to finish.
By contrast:
Call invokes the named function, waits for it to complete, and returns its error status.
Neither method executes in a goroutine directly*--this is left as an exercise for the caller (so an argument might be made that Go is a misnomer).
If you look at the source to Call, perhaps it is more clear:
func (client *Client) Call(serviceMethod string, args interface{}, reply
interface{}) error {
call := <-client.Go(serviceMethod, args, reply, make(chan *Call, 1)).Done
return call.Error
}
So in actuality, Call is a wrapper around Go, which waits for the operation to complete, whereas Go is the underlying function, which leaves waiting up to the caller.
*Clearly, in the background, a goroutine is involved somewhere, since this is a non-blocking operation.
Related
Essentially what the subject says. I'm interested to know whether each time the callback method is invoked the nats-lib for golang ensures that the callback will run on its very own goroutine (I'm 99% sure that it does but I need to make sure because I can't find any explicit piece of documentation in nats that conclusively states it so - feel free to copy paste any links if I've missed something).
If the callback does indeed run in it's own goroutine I'm also interested to know whether:
a. the same goroutine is used every time for a specific subscription
or b. an ephimeral goroutine is created for each firing of the callback: in this case the goroutine gets disposed of after the callback has done its work
When you call conn.Subscribe("some_subject", someCallback), it executes this piece of code if the callback is defined:
// If we have an async callback, start up a sub specific
// Go routine to deliver the messages.
if cb != nil {
sub.typ = AsyncSubscription
sub.pCond = sync.NewCond(&sub.mu)
go nc.waitForMsgs(sub)
}
It launches a goroutine for the created subscription. When a message arrives, it executes the defined callback inside the waitForMsgs goroutine.
In short, the answer is a., it uses the same goroutine every time for a specific subscription.
The io.Closer interface's documentation says:
The behavior of Close after the first call is undefined. Specific
implementations may document their own behavior.
What about a net.Conn's Close() function? Can I safely call it more than once?
Specifically I am wondering if I can defer c.Close() in case I early return for an error, but still explicitly call c.Close() rather than making the client wait for me to finish background processing.
Well, net.Conn is an interface, so it depends on the specific implementation.
I checked the implementation in TCPConn, UDPConn and IPConn structs. They seem to contain this implementation of net.Conn interface.
This implementation propagates the Close() call to its underlying file descriptor, which means that on Unix systems this function is then called.
We have to go even lower, since the next call is to the mutex that is used by the file descriptor.
If the connection is previously closed and mutex is freed, it seems that it returns false, and the file descriptor Close() function returns ErrNetClosing error.
So, I'll be free to say that you shouldn't call it more than once, as it will return an error in cases such as this one that I described. But it also may not error out in others. As the documentation says - it's undefined behavior.
I've wrapped a queue to implement the Writer and Reader interfaces (for pushing and popping, respectively).
I need to continuously listen to the queue, and handle every message that comes through. This is simple when the queue is represented as a channel, but more difficult otherwise:
loop:
for {
var data []byte
select {
case <-done:
break loop
case _, err := queue.Read(data):
fmt.Println(string(data))
}
}
What's the proper way to do this? Read here is blocking - it waits until the queue has a message.
Is there a better, more idiomatic way to achieve this?
It’s harder to take a synchronous API (like queue.Read as you described above) and make it asynchronous than it is to do the opposite.
The idea would be to create a new goroutine (using, for example go func() {...}) and have that goroutine execute the read and write the output to a channel.
Then the first goroutine would block on that channel and the one it’s already blocking on.
This has the potentially to leave orphaned resources for a little while if the read takes to long but if you have a synchronous API, it’s the best you can do.
Will context.Done() unblock when a context variable goes out of scope and cancel is not explicitly called?
Let's say I have the following code:
func DoStuff() {
ctx, _ := context.WithCancel(context.Background())
go DoWork(ctx)
return
}
Will ctx.Done() unblock in DoWork after the return in DoStuff()?
I found this thread, https://groups.google.com/forum/#!topic/golang-nuts/BbvTlaQwhjw, where the person asking how to use Context.Done() claims that context.Done() will unblock when the context variable leaves scope but no one validated this, and I didn't see anything in the docs.
No, it doesn't cancel automatically when the context leaves scope. Typically one calls defer cancel() (using the callback from ctx.WithCancel()) oneself to make sure that the context is cancelled.
https://blog.golang.org/context provides a good overview of how to use contexts correctly (including the defer pattern above). Also, the source code https://golang.org/src/context/context.go is quite readable and you can see there's no magic that would provide automatic cancellation.
"Unblocking" is not the clearest terminology. Done() returns a channel (or nil) that will receive a struct{} and/or close when the context is "cancelled". What exactly that chan is, or when it is sent on, is up to the individual implementation. It may be sent/closed at some fixed time as with WithDeadline, or manually done as with WithCancel.
The key though, is that this is never "automatic" or guaranteed to happen. If you make a context with WithCancel and read from the Done() channel, that read will block indefinitely until the Cancel() method is called. If that never happens, then you have a wasted goroutine and your application's memory will increase each time you do it.
Once the context is completely out of scope (no executing goroutine is listening to it or has a reference to the parent context), it will get garbage collected and everything will go away.
EDIT: After reading the source though, it looks like WithCancel and friends spawn goroutines to propigate the cancellation. Therefore you must make sure Cancel gets called at some point to avoid goroutine leaks.
In Go, go is the keyword to spawn a goroutine. However, it seems the keyword doesn't return any value, so how could I detect if the goroutine is spawned correctly?
It seems Go uses the following ways to check whether a func is successful or not.
value, err = myFunc(value)
Is there a similar usage for the go keyword to detect a creation error? It seems go will throw a runtime exception if it failed.
I want to make a test to find out the maximum number of goroutine I could create for a CPU.
As you already know:
value, err = myFunc(value)
is the idiomatic way to handle exceptions by returning the built-in error type. In a way you can compare it to a checked exception, I guess. In your case though, failing to spawn a new goroutine is more of a runtime exception. How golang handles those is by using panics. You can handle them in your code with the built-in recover() function, which will try to regain control of the execution flow. Without that the panic will go up the stack until it crashes the program.
Notice that recover() has to be called in a function which is being defered, those functions are pushed into a list and are always called at the end of the function in which they were defered - so even when the panic occurs they will be called, allowing you to call recover(). If you just try to call recover() at the end of your function (or well anywhere after you panicking subfunction) the execution will never reach it. If you can handle the panic (recover() doesn't return an err) so that your program can actually continue it will execute from the point where the function that threw the panic was.
Think the above blog post is enough but if you need more examples just comment here.
Also your system will most probably be bounded by RAM memory rather than CPU.
A goroutine creation is (more or less) just a memory allocation. You cannot in general catch memory allocation exceptions and it's the same with goroutines.
If your program runs out of memory, there's usually nothing you can do about it beyond quitting and restarting.
If the function you are using a go routine that returns an error, you can instead call go on an anonymous function and handle the error in the anonymous function.
go func() {
value, err := myFunc()
}()