I am trying to learn golang and i got a little piece of code that i do not understand why it gets stuck after some time.
package main
import "log"
func main() {
deliveryChann := make(chan bool, 10000)
go func() {
for {
deliveryChann <- true
log.Println("Sent")
}
}()
go func() {
for {
select {
case <-deliveryChann:
log.Println("received")
}
}
}()
go func() {
for {
select {
case <-deliveryChann:
log.Println("received")
}
}
}()
go func() {
for {
select {
case <-deliveryChann:
log.Println("received")
}
}
}()
for {
}
}
An basic start on how to investigate would suffice.
The main goroutine (running the for {} loop) is hogging the thread, and none of the other goroutines are able to execute because of it. If you change the end of your main function to:
for {
runtime.Gosched()
}
then the thread will be released and another goroutine made active.
func Gosched()
Gosched yields the processor, allowing other goroutines to run. It does not suspend the current goroutine, so execution resumes automatically.
-- https://golang.org/pkg/runtime/#Gosched
Order of execution of goroutings is undefined. Code gets stuck is legal. You can be more deterministic doing communication with main(). For example place
for {
deliveryChann <- true
log.Println("Sent")
}
in main() instead of go func()
Related
I have the following code for a module I'm developing and I'm not sure why the provider.Shutdown() function is never called when I called .Stop()
The main process does stop but I'm confused why this doesn't work?
package pluto
import (
"context"
"fmt"
"log"
"sync"
)
type Client struct {
name string
providers []Provider
cancelCtxFunc context.CancelFunc
}
func NewClient(name string) *Client {
return &Client{name: name}
}
func (c *Client) Start(blocking bool) {
log.Println(fmt.Sprintf("Starting the %s service", c.name))
ctx, cancel := context.WithCancel(context.Background())
c.cancelCtxFunc = cancel // assign for later use
var wg sync.WaitGroup
for _, p := range c.providers {
wg.Add(1)
provider := p
go func() {
provider.Setup()
select {
case <-ctx.Done():
// THIS IS NEVER CALLED?!??!
provider.Shutdown()
return
default:
provider.Run(ctx)
}
}()
}
if blocking {
wg.Wait()
}
}
func (c *Client) RegisterProvider(p Provider) {
c.providers = append(c.providers, p)
}
func (c *Client) Stop() {
log.Println("Attempting to stop service")
c.cancelCtxFunc()
}
Client code
package main
import (
"pluto/pkgs/pluto"
"time"
)
func main() {
client := pluto.NewClient("test-client")
testProvider := pluto.NewTestProvider()
client.RegisterProvider(testProvider)
client.Start(false)
time.Sleep(time.Second * 3)
client.Stop()
}
Because it's already chosen the other case before the context is cancelled. Here is your code, annotated:
// Start a new goroutine
go func() {
provider.Setup()
// Select the first available case
select {
// Is the context cancelled right now?
case <-ctx.Done():
// THIS IS NEVER CALLED?!??!
provider.Shutdown()
return
// No? Then call provider.Run()
default:
provider.Run(ctx)
// Run returned, nothing more to do, we're not in a loop, so our goroutine returns
}
}()
Once provider.Run is called, cancelling the context isn't going to do anything in the code shown. provider.Run also gets the context though, so it is free to handle cancellation as it sees fit. If you want your routine to also see cancellation, you could wrap this in a loop:
go func() {
provider.Setup()
for {
select {
case <-ctx.Done():
// THIS IS NEVER CALLED?!??!
provider.Shutdown()
return
default:
provider.Run(ctx)
}
}
}()
This way, once provider.Run returns, it will go through the select again, and if the context has been cancelled, that case will be called. However, if the context hasn't been cancelled, it'll call provider.Run again, which may or may not be what you want.
EDIT:
More typically, you'd have one of a couple scenarios, depending on how provider.Run and provider.Shutdown work, which hasn't been made clear in the question, so here are your options:
Shutdown must be called when the context is cancelled, and Run must only be called once:
go func() {
provider.Setup()
go provider.Run(ctx)
go func() {
<- ctx.Done()
provider.Shutdown()
}()
}
Or Run, which already receives the context, already does the same thing as Shutdown when the context is cancelled, and therefore calling Shutdown when the context is cancelled is wholly unnecessary:
go provider.Run(ctx)
Why panic stop occurring if we replace global variable with true?
package main
import (
"time"
)
// If here written false, the panic is expected some times.
// But if we write true here, then panic will never happen.
// Why?
var value = false
func main() {
go func() { for { value = true } }()
time.Sleep(time.Second)
for {
if !value {
panic("FALSE!")
}
}
}
It is a race condition. In such situation you need to use WaitGroup.
Using time sleep is dangerous as we cannot predict how go routines are executed by the scheduler.
See the following code:
func main() {
var wait sync.WaitGroup
wait.Add(1);
go func() {
for {
if !value {
value = true
wait.Done()
}
}
}()
wait.Wait()
//time.Sleep(time.Second)
for {
if !value {
panic("FALSE!")
}
}
}
I am unable to get prprpus scheduler library with Go to work. I was trying to recreate the functionality of JavaScript's setinterval. The for loop should not block the scheduled job right?
package main
import (
"fmt"
"github.com/prprprus/scheduler"
)
func keepAlive2() {
fmt.Println("Keep alive 2")
}
func main() {
s, schedulerErr := scheduler.NewScheduler(1000)
if schedulerErr != nil {
panic(schedulerErr)
}
s.Every().Second(1).Do(keepAlive2)
for {
}
}
Busy-wait tight loops like
for {
}
will not allow other goroutines execute. It will not yield. If you need to wait, use one of the synchronization primitives, such as:
ch:=make(chan struct{})
<- ch
This question already has answers here:
How to stop a goroutine
(9 answers)
Closed 3 years ago.
I have 2 goroutines, g is used to detect the condition when f should stop, f checks whether it should stop in each iteration before doing the actual processing. In other languages such as Java, I would use a thread-safe shared variable, like the following code:
func g(stop *bool) {
for {
if check_condition() {
*stop = true
return
}
}
}
func f(stop *bool) {
for {
if *stop {
return
}
do_something()
}
}
func main() {
var stop = false
go g(&stop)
go f(&stop)
...
}
I know the code above is not safe, but if I use channel to send stop from g to f, f would be blocked on reading from the channel, this is what I want to avoid. What is the safe and idiomatic way of doing this in Go?
Use channel close to notify other goroutines of a condition. Use select with a default clause to avoid blocking when checking for the condition.
func g(stop chan struct{}) {
for {
if check_condition() {
close(stop)
return
}
}
}
func f(stop chan struct{}) {
for {
select {
case <-stop:
return
default:
do_something()
}
}
}
func main() {
var stop = make(chan struct{})
go g(stop)
go f(stop)
}
It also works to send a value to a channel with capacity greater than zero, but closing the channel extends to supporting multiple goroutines.
The way is to use a select statement with a default clause (see this example).
So f would look something like:
func f(stop chan bool) {
select {
case s := <- stop:
if s {
return
}
default:
do_something()
}
}
I have a goroutine that will be run multiple times. But it can only run one at a time (single instance). What is the correct/idiomatic way to make sure a certain goroutine can run only one at a time?
Here is my contrived example code to illustrate the point:
func main() {
// Contrived example!!!!!!
// theCaller() may be run at multiple, unpredictable times
// theJob() must only be run one at a time
go theCaller()
go theCaller()
go theCaller()
}
func theCaller() {
if !jobIsRunning { // race condition here!
jobIsRunning = true
go theJob()
}
}
var jobIsRunning bool
// Can run multiple times, but only one at a time
func theJob() {
defer jobDone()
do_something()
}
func jobDone() {
jobIsRunning = false
}
Based on question and other comments from the OP, it looks like the goal is to start a new job if and only if a job is not already running.
Use a boolean variable protected by a sync.Mutex to record the running state of of the job. Set the variable to true when starting a job and to false when the job completes. Test this variable to determine if a job should be started.
var (
jobIsRunning bool
JobIsrunningMu sync.Mutex
)
func maybeStartJob() {
JobIsrunningMu.Lock()
start := !jobIsRunning
jobIsRunning = true
JobIsrunningMu.Unlock()
if start {
go func() {
theJob()
JobIsrunningMu.Lock()
jobIsRunning = false
JobIsrunningMu.Unlock()
}()
}
}
func main() {
maybeStartJob()
maybeStartJob()
maybeStartJob()
}
The lower-level sync/atomic package can also be used and may have better performance than using a mutex.
var jobIsRunning uint32
func maybeStartJob() {
if atomic.CompareAndSwapUint32(&jobIsRunning, 0, 1) {
go func() {
theJob()
atomic.StoreUint32(&jobIsRunning, 0)
}()
}
}
The sync/atomic package documentation warns that the functions in the package require great care to use correctly and that most applications should use the sync package.