This is a creation in Article, where the information may have evolved or changed.
Original: Go programming tips--goroutine's elegant control
Goroutine
Is the most important mechanism of the go language, which Goroutine
abstracts the complex asynchronous IO calls into synchronous calls, conforms to the normal sequence of human thinking, greatly simplifies the difficulty of IO programming. As with the thread, the Goroutine
exit mechanism should be well controlled in order to master the basic usage Goroutine
. This paper introduces a kind Goroutine
of exit idea.
There Goroutine
are usually two cases of blocking:
IO operations, such as the right Socket
Read
.
channel
Operation. The reading and writing of a Chan can be blocked Goroutine
.
For Case 1, only the corresponding descriptor needs to be closed, and the blocked Goroutine
nature will be awakened.
The focus of the discussion is 2. Concurrent programming, which Goroutine
provides a channel
mechanism, similar to a pipeline, where the channel
writer writes data to the reader and reads the data from it. If there channel
is no data in it, the reader will block until there is data, and if the channel
data is full, the writer will be blocked by the inability to continue writing the data.
If writer and reader both behave as one and always work throughout the lifetime of the application, Goroutine
how do you notify them to terminate before the application is finished? In go, it is not recommended to force termination like the abort thread Goroutine
. Thus, abstractly, it is necessary to retain an entry that communicates with writer or reader to inform them of termination.
Let's read reader first. The first thing we can think close
of is to use the function to close the reading channel
so that you can wake reader and exit. But considering that the close
writer is not well handled (because writer tries to write an already close channel, an exception is thrown). Therefore, we need to design an additional read-only channel
for notification:
type routineSignal struct { done <-chan struct{}}
routineSignal
Should be generated externally and passed to reader, for example:
func (r *reader)init(s *routineSignal) { r.signal = s}
In the reader's loop, it can be written like this:
func (r *reader)loop() { for { select { case <-r.signal.done: return case <-r.queue: .... } }}
When the need to terminate Goroutine
only need to close this extra channel
:
close(signal.done)
It looks perfect, and it can handle most of the situation. There is a drawback, though, that we can expect to close
wake up and Goroutine
exit, but we don't know Goroutine
when to complete the exit, because Goroutine
there may be some aftercare before the exit, which we need sync.WaitGroup
. Retrofit routineSignal
:
type routineSignal struct { done chan struct{} wg sync.WaitGroup}
Add a sync. Waitgroup example, in the Goroutine
beginning of work, the WG plus 1, Goroutine
before exiting, the WG minus 1:
func (r *reader)loop() { r.signal.wg.Add(1) defer r.signal.wg.Done() for { select { case <-r.signal.done: return case <-r.queue: .... } }}
External, just wait to WaitGroup
return:
close(signal.done)signal.wg.Wait()
As long as Wait()
the return can be concluded Goroutine
.
It is not difficult to deduce that this method can be used for writer. So, to summarize, we created a structure called routinesignal
, which contains a Chan
to notify goroutine
end, including a Waitgroup
for Goroutine
notifies the outside to complete the cleanup. This way, an instance of this structure gracefully terminates Goroutine
, and you can also ensure that Goroutine
terminates successfully.