This problem comes from a test code I saw when reading the fabric source code. Enable coroutines in a function, and then the function exited. Since it has not been handled like this normally, and affected by the original C++ function domain, I think that the function exits, and the subcoroutines should also exit.
This is actually caused by my inadequate understanding of go coroutines. Go's coroutine scope is not in a certain function. Of course, if that function is a main function, it will meet the requirements.
This code is the test code of the solo algorithm:
func goWithWait(target func()) *waitableGo { wg := &waitableGo{ done: make(chan struct{}), } go func() { target()//The coroutine will block here close()// Used to inform others }() //The outside ends, but the inside is not over yet? return wg } // This test checks that if consenter is halted before a timer fires, nothing is actually written. func TestHaltBeforeTimeout(t *) { batchTimeout, _ := ("1ms") //The structure of support is not clear support := &{ Blocks: make(chan *), BlockCutterVal: (), SharedConfigVal: &{BatchTimeoutVal: batchTimeout}, } defer close() bs := newChain(support) //It is the start function of the solo algorithm, it is a dead loop, processing function wg := goWithWait() defer ()//Abort syncQueueMessage(testMessage, bs, ) () select { case <-: ("Expected no invocations of Append") case <-: } }
After encountering this problem, I wrote several tests:
A simple function exit will not affect the coroutine
package main import "fmt" var ch chan int func test() int { ch = make(chan int) go func() { for { (<-ch) ("hello") } ("aaaa") }() //If you don't block, will go func() not exit abnormally? //The coroutine is not a function and will not exit because of the exit of this function //test() starts a deadloop child coroutine, which will be forced to exit after the main coroutine main ends return 0 } func main() { c := test() ch <- 10 ("c", c) }
I often write test demo of coroutines directly in main. The main exit will end the main coroutine, and then the child coroutine will be forced to end. Generally, I will not encounter the above-mentioned problem of exiting ordinary functions, and I have not thought about it carefully, so I am a little confused when analyzing the source code.
The child coroutine initiates the child coroutine, and the exit of the parent coroutine does not affect the child coroutine.
liudeMacBook-Pro:~ liu$ cat package main import ( "fmt" "time" ) func test() { go func() { //Father Co-Process defer func() { ("exit dad") }() go func() { //Subcord defer func() { ("exit kid") }() }() }() } func main() { test() () } liudeMacBook-Pro:~ liu$ go run exit dad exit kid
Supplement: Parent-child coroutine life cycle problem in golang, and elegantly closing child coroutines through context
background
Last time I implemented a distributed lock based on mysql. Today, after testing, I found a problem. The main problem is that there is a problem with the logic of the coroutine that continuously acquires the lock. After the coroutine that acquires the lock has been hung up, the newly generated coroutine used to continuously update the lock will not exit, which will cause the lock to be released. The reasons are as follows
reason
You can explain it by following the code
("main function start...") go func() { ("The parent coroutine starts...") go func() { for { ("Sub-coroutine is being executed...") timer := ( * 2) <- } }() (*5) ("Parent coroutine Exit...") }() (*10) ("main function exit")
main function Start...
Parent coroutine starts...
Subcoroutine execution...
Subcoroutine execution...
Subcoroutine execution...
Parent coroutine Exit...
Subcoroutine execution...
Subcoroutine execution...
main function Exit
From this we can see:
main function exits, all coroutines exit
The coroutine has no father-son relationship, that is, a new coroutine is opened in the parent coroutine. If the parent coroutine exits, it will not affect the child coroutine.
Solution
Solved through context, of course, it can also be solved through channel pipeline. The context solution is as follows:
("main function start...") go func() { ctx, cancel := (()) defer cancel() ("The parent coroutine starts...") go func(ctx ) { for { for { select { case <-(): ("Sub-coroutine accepts stop signal...") return default: ("Sub-coroutine is being executed...") timer := ( * 2) <- } } } }(ctx) (*5) ("Parent coroutine Exit...") }() (*10) ("main function exit")
main function Start...
Parent coroutine starts...
Subcoroutine execution...
Subcoroutine execution...
Subcoroutine execution...
Parent coroutine Exit...
Subcoroutine accepts stop signal...
main function Exit
The above is personal experience. I hope you can give you a reference and I hope you can support me more. If there are any mistakes or no complete considerations, I would like to give you advice.