In this article, php editor Xiaoxin will introduce an important issue about Go programs: the situation of exiting before the goroutine work is completed. In the Go language, goroutine is a lightweight thread that can execute tasks concurrently. However, when our program may exit before the goroutine work is completed, we need to understand how to handle this situation to ensure that our program completes the task correctly. In the following content, we will explore this problem and provide some solutions to solve it.
Question Content
I'm having trouble understanding how to properly block and close channels. I'm starting an arbitrary number of workers and I'm finding that my main function either exits before the workers complete or hangs due to unclosed channels. I need a better way to stop the worker from reading the channel without exiting the main channel, and then gracefully close the channel when finished to end the loop. Any attempts I make end in deadlock.
I tried a few things including using a wait group, but the problem persists. I noticed that by adding time.sleep
the program works as expected, but commenting it out results in no work being done.
time.sleep(time.duration(10 * time.second))
This is a runnable example https://go.dev/play/p/qhqnj-ajqbi which preserves sleep
. This is the broken code with the sleep timeout commented out.
package main import ( "fmt" "sync" "time" ) // some complicated work func do(num int, ch chan<- int) { time.sleep(time.duration(500 * time.millisecond)) ch <- num } func main() { results := make(chan int) // for some number of required complicated work for i := 0; i < 53; i++ { go do(i, results) } var wg sync.waitgroup // start 3 workers which can process results for i := 0; i < 3; i++ { wg.add(1) go func(id int) { defer wg.done() worker(id, results) }(i) } // handle closing the channel when all workers complete go func() { wg.wait() close(results) }() //time.sleep(time.duration(10 * time.second)) fmt.println("donezo") } // process the results of do() in a meaningful way func worker(id int, ch <-chan int) { fmt.println("starting worker", id) for i := range ch { fmt.println("channel val:", i) } }
I also tried moving defer wg.done()
inside the worker()
func but it's the same problem and doesn't work without sleep.
// process the results of do() in a meaningful way func worker(wg *sync.WaitGroup, id int, ch <-chan int) { fmt.Println("starting worker", id) defer wg.Done() for i := range ch { fmt.Println("channel val:", i) } }
Did I choose the wrong paradigm, or am I just using the wrong paradigm?
Workaround
I originally asked "Can I make some small adjustments to my code to make it work? Or do I have to rethink this problem?" I The answer found is that, yes, there is a small adjustment.
I had to learn an interesting basic concept about channels: you can read data from a closed channel, i.e. drain the channel. As mentioned in my original example range
never terminates because I can't find a good place to close the channel, and even when I force it in other creative ways the program behaves poorly Behavior
- Exited without processing all content in the channel
- Deadlock or sending on closed channel
This is due to a subtle difference in the "real" code where the time required to process the channel contents is longer than the time required to populate the channel and Things are out of sync.
Since there is no clear practical way to close the channel in my sender (which is recommended in 99% of channel tutorials), when you have multiple workers reading the channel and the workers don't know about it, by It's actually acceptable to do this with a goroutine in main where the last value is read.
solution
I wrapped the worker in its own sync.waitgroup
and used worker.wait()
to block the program exit, thus allowing the work to "finish". When there is no more data to send, I close()
the channel independently, i.e. I block by waiting for the writer to finish using their own wait group. close Provides a termination case for range loops because when the channel's default value is returned, i.e. the eof type is reached when the end of the channel is reached, it will end. A blocking rendezvous channel has no endpoint until it is closed.
My take on this is that if you don't know how many values will be pushed in parallel, go has no way of knowing the length of the unbuffered channel because it's in scope, until you close it. . Since it's closed, it means reading whatever is left until the termination value or the end. workers.wait()
will block until completed.
Examples of resolved operations https://www.php.cn/link/2bf0ccdbb4d3ebbcb990af74bd78c658
Example of reading closed channel https://www.php.cn/link/d5397f1497b5cdaad7253fdc92db610b
Output
filling 0 filling 1 filling 2 filling 3 filling 4 filling 5 filling 6 filling 7 filling 8 filling 9 closed empyting 0 empyting 1 empyting 2 empyting 3 empyting 4 empyting 5 empyting 6 empyting 7 empyting 8 empyting 9
The above is the detailed content of Go program exits before goroutine work completes. For more information, please follow other related articles on the PHP Chinese website!

Mastering the strings package in Go language can improve text processing capabilities and development efficiency. 1) Use the Contains function to check substrings, 2) Use the Index function to find the substring position, 3) Join function efficiently splice string slices, 4) Replace function to replace substrings. Be careful to avoid common errors, such as not checking for empty strings and large string operation performance issues.

You should care about the strings package in Go because it simplifies string manipulation and makes the code clearer and more efficient. 1) Use strings.Join to efficiently splice strings; 2) Use strings.Fields to divide strings by blank characters; 3) Find substring positions through strings.Index and strings.LastIndex; 4) Use strings.ReplaceAll to replace strings; 5) Use strings.Builder to efficiently splice strings; 6) Always verify input to avoid unexpected results.

ThestringspackageinGoisessentialforefficientstringmanipulation.1)Itofferssimpleyetpowerfulfunctionsfortaskslikecheckingsubstringsandjoiningstrings.2)IthandlesUnicodewell,withfunctionslikestrings.Fieldsforwhitespace-separatedvalues.3)Forperformance,st

WhendecidingbetweenGo'sbytespackageandstringspackage,usebytes.Bufferforbinarydataandstrings.Builderforstringoperations.1)Usebytes.Bufferforworkingwithbyteslices,binarydata,appendingdifferentdatatypes,andwritingtoio.Writer.2)Usestrings.Builderforstrin

Go's strings package provides a variety of string manipulation functions. 1) Use strings.Contains to check substrings. 2) Use strings.Split to split the string into substring slices. 3) Merge strings through strings.Join. 4) Use strings.TrimSpace or strings.Trim to remove blanks or specified characters at the beginning and end of a string. 5) Replace all specified substrings with strings.ReplaceAll. 6) Use strings.HasPrefix or strings.HasSuffix to check the prefix or suffix of the string.

Using the Go language strings package can improve code quality. 1) Use strings.Join() to elegantly connect string arrays to avoid performance overhead. 2) Combine strings.Split() and strings.Contains() to process text and pay attention to case sensitivity issues. 3) Avoid abuse of strings.Replace() and consider using regular expressions for a large number of substitutions. 4) Use strings.Builder to improve the performance of frequently splicing strings.

Go's bytes package provides a variety of practical functions to handle byte slicing. 1.bytes.Contains is used to check whether the byte slice contains a specific sequence. 2.bytes.Split is used to split byte slices into smallerpieces. 3.bytes.Join is used to concatenate multiple byte slices into one. 4.bytes.TrimSpace is used to remove the front and back blanks of byte slices. 5.bytes.Equal is used to compare whether two byte slices are equal. 6.bytes.Index is used to find the starting index of sub-slices in largerslices.

Theencoding/binarypackageinGoisessentialbecauseitprovidesastandardizedwaytoreadandwritebinarydata,ensuringcross-platformcompatibilityandhandlingdifferentendianness.ItoffersfunctionslikeRead,Write,ReadUvarint,andWriteUvarintforprecisecontroloverbinary


Hot AI Tools

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Undress AI Tool
Undress images for free

Clothoff.io
AI clothes remover

Video Face Swap
Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Article

Hot Tools

Zend Studio 13.0.1
Powerful PHP integrated development environment

SublimeText3 Linux new version
SublimeText3 Linux latest version

SAP NetWeaver Server Adapter for Eclipse
Integrate Eclipse with SAP NetWeaver application server.

MinGW - Minimalist GNU for Windows
This project is in the process of being migrated to osdn.net/projects/mingw, you can continue to follow us there. MinGW: A native Windows port of the GNU Compiler Collection (GCC), freely distributable import libraries and header files for building native Windows applications; includes extensions to the MSVC runtime to support C99 functionality. All MinGW software can run on 64-bit Windows platforms.

DVWA
Damn Vulnerable Web App (DVWA) is a PHP/MySQL web application that is very vulnerable. Its main goals are to be an aid for security professionals to test their skills and tools in a legal environment, to help web developers better understand the process of securing web applications, and to help teachers/students teach/learn in a classroom environment Web application security. The goal of DVWA is to practice some of the most common web vulnerabilities through a simple and straightforward interface, with varying degrees of difficulty. Please note that this software
