Sync between 2 goroutines - go

My task is to sync 2 goroutines so the output should look like that:
foobarfoobarfoobarfoobar
.The issue is that when I call them they come out completely randomized. This is my code:
package main
import (
"fmt"
"sync"
"time"
)
type ConcurrentPrinter struct {
sync.WaitGroup
sync.Mutex
}
func (cp *ConcurrentPrinter) printFoo(times int) {
cp.WaitGroup.Add(times)
go func() {
cp.Lock()
fmt.Print("foo")
cp.Unlock()
}()
}
func (cp *ConcurrentPrinter) printBar(times int) {
cp.WaitGroup.Add(times)
go func() {
cp.Lock()
fmt.Print("bar")
cp.Unlock()
}()
}
func main() {
times := 10
cp := &ConcurrentPrinter{}
for i := 0; i <= times; i++ {
cp.printFoo(i)
cp.printBar(i)
}
time.Sleep(10 * time.Millisecond)
}

As outlined in the comments, using goroutines may not be the best use case for what you are trying to achieve - and thus this may be an XY problem.
Having said that, if you want to ensure two independent goroutines interleave their work in an alternating sequence, you can implement a set of "ping-pong" mutexs:
var ping, pong sync.Mutex
pong.Lock() // ensure the 2nd goroutine waits & the 1st goes first
go func() {
for {
ping.Lock()
foo()
pong.Unlock()
}
}()
go func() {
for {
pong.Lock()
bar()
ping.Unlock()
}
}()
https://go.dev/play/p/VO2LoMJ8fek

Using channel:
func printFoo(i int, ch chan<- bool, wg *sync.WaitGroup) {
wg.Add(1)
go func() {
defer wg.Done()
fmt.Print("foo")
ch <- true
}()
}
func printBar(i int, ch chan<- bool, wg *sync.WaitGroup) {
wg.Add(1)
go func() {
defer wg.Done()
fmt.Print("bar")
ch <- true
}()
}
func main() {
times := 4
firstchan := make(chan bool)
secondchan := make(chan bool)
var wg sync.WaitGroup
for i := 0; i <= times; i++ {
printFoo(i, firstchan, &wg)
<-firstchan
printBar(i, secondchan, &wg)
<-secondchan
}
wg.Wait()
}
https://go.dev/play/p/MlZ9dHkUXGb

Related

waitgroup.Wait() causing dead lock

I'm trying to figure out why I have a dead lock with waitgroup.Wait()
package main
import (
"fmt"
"sync"
)
var wg sync.WaitGroup
func foo(c chan int, i int) {
defer wg.Done()
c <- i
}
func main() {
ch := make(chan int)
for i := 0; i < 10; i++ {
wg.Add(1)
go foo(ch, i)
}
wg.Wait()
close(ch)
for item := range ch {
fmt.Println(item)
}
}
When I run it like this, it prints fatal error: all goroutines are asleep - deadlock!
I tried to change ch to a buffered channel and that solved the problem. But I really want to know why is there a dead lock.
I've commented out the parts where your program's logic is not correct:
package main
import (
"fmt"
"sync"
)
var wg sync.WaitGroup
func foo(c chan int, i int) {
defer wg.Done()
c <- i
}
func main() {
ch := make(chan int) // unbuffered channel
for i := 0; i < 10; i++ {
wg.Add(1)
go foo(ch, i)
}
// wg.Wait is waiting for all goroutines to finish but that's
// only possible if the send to channel succeeds. In this case,
// it is not possible as your receiver "for item := range ch" is below
// this. Hence, a deadlock.
wg.Wait()
// Ideally, it should be the sender's duty to close the channel.
// And closing a channel before the receiver where the channel
// is unbuffered is not correct.
close(ch)
for item := range ch {
fmt.Println(item)
}
}
Corrected program:
package main
import (
"fmt"
"sync"
)
var wg sync.WaitGroup
func foo(c chan int, i int) {
defer wg.Done()
c <- i
}
func main() {
ch := make(chan int)
go func() {
for item := range ch {
fmt.Println(item)
}
}()
for i := 0; i < 10; i++ {
wg.Add(1)
go foo(ch, i)
}
wg.Wait()
close(ch)
}

Simplest concurrent loop with bounded concurrency

I'm looking for the simplest code for looping over a dataset in parallel. The requirements are that the number of goroutines is fixed and that they can return an error code. The following is a quick attempt which doesn't work, since the loops will deadlock as both goroutines are blocking on the error channel
package main
import (
"fmt"
"sync"
)
func worker(wg *sync.WaitGroup, intChan chan int, errChan chan error) {
defer wg.Done()
for i := range intChan {
fmt.Printf("Got %d\n", i)
errChan <- nil
}
}
func main() {
ints := []int{1,2,3,4,5,6,7,8,9,10}
intChan := make(chan int)
errChan := make(chan error)
wg := new(sync.WaitGroup)
for i := 0; i < 2; i++ {
wg.Add(1)
go worker(wg, intChan, errChan)
}
for i := range ints {
intChan <- i
}
for range ints {
err := <- errChan
fmt.Printf("Error: %v\n", err)
}
close(intChan)
wg.Wait()
}
What is the simplest pattern for doing this?
Listen for errors in a goroutine:
go func() {
for err:=range errChan {
// Deal with err
}
}()
for i := 0; i < 2; i++ {
wg.Add(1)
go worker(wg, intChan, errChan)
}
for i := range ints {
intChan <- i
}
close(errChan) // Error listener goroutine terminates after this

How to start and stop a function

I have a go function processing that use two distinct goroutines. produce will push some data into a channel and consume will read these data. Here is an example:
type MyObject struct{
...
}
func processing() {
var wg sync.WaitGroup
dataChannel := make(chan MyObject, 5)
wg.Add(2)
go produce(wg, dataChannel)
go consume(wg, dataChannel)
wg.Wait()
}
func produce (wg *sync.WaitGroup, dataChannel chan MyObject){
for{
// Produce data in dataChannel
}
}
func consume (wg *sync.WaitGroup, dataChannel chan MyObject){
for{
// Consume data from dataChannel
}
}
I want my processing function to be started and stoped by an HTTP call. So I am looking to do something as follow:
func main() {
// echo instance
e := echo.New()
e.GET("/", startProcessing)
e.Logger.Fatal(e.Start(":8099"))
}
func startProcessing(c echo.Context) error{
command := c.QueryParam("command")
if(command == "start"){
processing()
}else if(command == "stop"){
if (/* ? processing is running ? */){
/* ? stop processing process? */
}
}
}
What is the correct way to do this with Go?
Here how to start and stop a function using context, try this:
package main
import (
"context"
"fmt"
"sync"
"time"
)
func main() {
ctx, cancel := context.WithCancel(context.Background())
var wg sync.WaitGroup
dataChannel := make(chan MyObject, 5)
wg.Add(2)
go produce(ctx, &wg, dataChannel)
go consume(&wg, dataChannel)
time.Sleep(1 * time.Second)
cancel() // cancel when we are finished consuming data
wg.Wait()
}
func produce(ctx context.Context, wg *sync.WaitGroup, dataChannel chan MyObject) {
defer wg.Done()
i := 1
for {
select {
case <-ctx.Done():
close(dataChannel)
return // returning not to leak the goroutine
case dataChannel <- MyObject{i}:
i++
time.Sleep(250 * time.Millisecond)
}
}
}
func consume(wg *sync.WaitGroup, dataChannel chan MyObject) {
defer wg.Done()
for v := range dataChannel {
fmt.Println(v)
}
}
type MyObject struct {
i int
}
For HTTP you need to do it yourself!
It needs to have some concurrent safe ID or map or something to keep track of how many functions you called and then call a cancel() to stop it.

Trying to end goroutine using close(ch) but end up running infinitely

I am trying to end multiple goroutines once another goroutine closes a channel. However, I am ending up into infinite loop after close signal is received. I can't figure out why.
I know that it is possible using context.Context but I was trying out by closing channels.
Go Playground: https://play.golang.org/p/C6pcYgGLnG9
package main
import (
"fmt"
"time"
"sync"
)
func runner(id int, ch <-chan struct{}, wg *sync.WaitGroup) {
for {
select {
case <-time.Tick(time.Second):
fmt.Println("worker ", id)
case <- ch:
fmt.Println("closing worker ", id)
break
}
}
wg.Done()
}
func main() {
fmt.Println("Hello, playground")
ch := make(chan struct{})
var wg sync.WaitGroup
wg.Add(1)
go runner(1, ch, &wg)
wg.Add(1)
go runner(2, ch, &wg)
time.Sleep(5*time.Second)
close(ch)
wg.Wait()
}
The problem is the scope of your break:
func runner(id int, ch <-chan struct{}, wg *sync.WaitGroup) {
for {
select {
case <-time.Tick(time.Second):
fmt.Println("worker ", id)
case <- ch:
fmt.Println("closing worker ", id)
break
}
}
wg.Done()
}
You want to break out of the for loop, but you're actually only breaking out of the select. To remedy this, you have two choices:
Add a label to your for loop, and break from it explicitly:
func runner(id int, ch <-chan struct{}, wg *sync.WaitGroup) {
loop: // <---------- add a label
for {
select {
case <-time.Tick(time.Second):
fmt.Println("worker ", id)
case <-ch:
fmt.Println("closing worker ", id)
break loop // <---------- and break from it explicitly
}
}
wg.Done()
}
Probably a more idiomatic and robust solution, simply return when you're done. This means the wg.Done() call must be deferred.
func runner(id int, ch <-chan struct{}, wg *sync.WaitGroup) {
defer wg.Done() // <--- Defer the wg.Done() call, so it happens on return
for {
select {
case <-time.Tick(time.Second):
fmt.Println("worker ", id)
case <-ch:
fmt.Println("closing worker ", id)
return // <--- replace `break` with `return`
}
}
}

go routine deadlock with single channel

I started learning go recently and I am stuck on a problem.
I have a simple go routine which either returns or pushes value to a channel.
And my main fn delegates work to this routine till it meets condition or data is exhausted.
This code seem to deadlock on "found" channel. What am I doing wrong?
There are multiple workers
Item can be found in more than one worker at the same time
Once item is found, all workers should be stopped.
.
func workerRoutine(data Data, found chan bool, wg *sync.WaitGroup){
defer (*wg).Done()
// data processing
// return on false
// multiple routines can set this at the same time
found <-true
}
func main {
// ....
found:=make(chan bool)
var wg sync.WaitGroup
itemFound:=false
Loop:
for i:=0; i<limit; i++ {
select {
case <-found:
itemFound = true
break Loop
default:
if(some_check) {
wg.Add(1)
go workerRoutine(mdata,found,&wg)
}
}
}
wg.Wait()
// use itemFound
}
One possible solution is to avoid select statement and use separate goroutine for receiver (or sender, or both).
Example:
package main
import "sync"
func worker(res chan bool, wg *sync.WaitGroup) {
res <- true
wg.Done()
}
func receiver(res chan bool, wg *sync.WaitGroup) {
for range res {
}
wg.Done()
}
func main() {
var wg, wg2 sync.WaitGroup
wg.Add(1)
wg2.Add(10)
found := make(chan bool)
go receiver(found, &wg)
for i := 0; i < 10; i++ {
go worker(found, &wg2)
}
wg2.Wait()
close(found)
wg.Done()
}

Resources