H
H
helixly2017-08-04 11:10:18
go
helixly, 2017-08-04 11:10:18

How to implement a run queue of goroutines?

Good afternoon! The task requires to implement a request execution queue.
In general, the situation is as follows. The web service accepts a json array of functions to be executed. The array itself is very similar to JSON-RPC with the addition of one more propertywait

Here is an example
[
    {
        "method": "controller:method1",
        "params": {},
        "id": 1
    },
    {
        "method": "controller:method2",
        "params": {},
        "id": 2,
        "wait": 1
    }
]


Sketched an example of the implementation of the expectation. Do not judge strictly for the "beautifulness" of the code, this is just an example.
There he is
package main

import (
  "fmt"
  "sync"
  "time"
)


type chain struct {
  Req       *req
  Wait      chan bool
  Signal    chan bool
  IsWaiting bool
}

type req struct {
  Method func()
  ID     int
  W      int
}

func main() {

  chainigs := make(map[int]*chain)

  var fncs []*req

  r1 := &req{
    Method: func1,
    ID:     1,
    W:      0,
  }

  r2 := &req{
    Method: func2,
    ID:     2,
    W:      1,
  }
  r3 := &req{
    Method: func3,
    ID:     3,
    W:      0,
  }

  fncs = append(fncs, r1, r2, r3)

  for _, req := range fncs {

    ch := &chain{}

    ch.Req = req

    signalChan := make(chan bool)

    ch.Signal = signalChan

    chainigs[req.ID] = ch

    if waitChan, ok := chainigs[req.W]; ok {
      ch.Wait = waitChan.Signal
      ch.IsWaiting = true
    } else {
      ch.IsWaiting = false
    }

    chainigs[req.ID] = ch
  }

  var wg sync.WaitGroup

  for _, schain := range chainigs {

    wg.Add(1)

    go func(c *chain) {

      if c.IsWaiting {
        select {
        case <-c.Wait:
          c.Req.Method()
          wg.Done()
          go func() {
            c.Signal <- true
            close(c.Signal)
          }()
        }
      } else {
        c.Req.Method()
        wg.Done()
        c.Signal <- true
      }
    }(schain)
  }

  wg.Wait()
}

func func1() {
  time.Sleep(2 * time.Second)
  fmt.Println("FORM 1")
}

func func2() {
  fmt.Println("FROM 2")
}

func func3() {
  time.Sleep(2 * time.Second)
  fmt.Println("FROM 3")
}


Here's the idea. The structure chainhas two fields with channels Waitand Signal.
Wait- will hold the channel Signalof another structure to be expected to execute. Well, then start in the goroutine cycle, and check if the chain should wait for the execution of another, then we are waiting for the signal. If not, then we simply execute the function and send a termination signal. With such an implementation, it is possible to build chains of calls, and in general the program works correctly. I'm wondering how correct this approach can be considered? Perhaps there are patterns that are much better and more beautiful for realizing this possibility?

Answer the question

In order to leave comments, you need to log in

2 answer(s)
A
Alexander Pavlyuk, 2017-08-04
@helixly

Maybe it's more convenient for you to attach weightgroups to processes and run them all at the same time?
https://golang.org/pkg/sync/#WaitGroup
1. We push a weight group into the structure for each task, add(1) to it, and after execution we do Done().
2. For each dependent task, before execution, we do Wait () for the weight groups of those tasks that it must wait for.
3. ????
4. PROFIT!!!

R
RidgeA, 2017-08-04
@RidgeA

I don’t know which is better, but I would sort it in order of dependency and execute the methods in turn.

[
    {
        "method": "controller:method1",
        "params": {},
        "id": 1
    },
    {
        "method": "controller:method2",
        "params": {},
        "id": 2,
        "wait": 1
    },
    {
        "method": "controller:method2",
        "params": {},
        "id": 3,
        "wait": 1
    },
    {
        "method": "controller:method2",
        "params": {},
        "id": 4,
        "wait": 2
    }
    {
        "method": "controller:method2",
        "params": {},
        "id": 5
    }
]

bring to mind
[
  [
    {
        "method": "controller:method1",
        "params": {},
        "id": 1
    },
    {
        "method": "controller:method2",
        "params": {},
        "id": 5
    }  
  ],
  [
    {
        "method": "controller:method2",
        "params": {},
        "id": 2,
        "wait": 1
    },
    {
        "method": "controller:method2",
        "params": {},
        "id": 3,
        "wait": 1
    },
  ],
  [
    {
        "method": "controller:method2",
        "params": {},
        "id": 4,
        "wait": 2
    }  
  ]
]

and then something like this
for _, step := range steps {
  for _, f : range step {
    f.method(f.params)
  }
}

something like graphs and topological sort

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question