Skip to content

Instantly share code, notes, and snippets.

@evalphobia
Last active December 23, 2019 04:19
Show Gist options
  • Save evalphobia/dbefdc6908fc7e61f3b0b4dde2b33076 to your computer and use it in GitHub Desktop.
Save evalphobia/dbefdc6908fc7e61f3b0b4dde2b33076 to your computer and use it in GitHub Desktop.
How to use context.Cancel: Execute multiple process and gets the single fastest result.
package example
import (
"context"
)
// Executor is example process.
// This might be, downloading data via http / reading data from local big file / waiting data from client etc...
type Executor interface {
Exec() (data interface{}, err error) // this might take a long time.
}
// ExecResult is example result.
type ExecResult struct {
ImportantData interface{}
Err error
}
// ====================
// ↓↓↓ Main Process ↓↓↓
// ====================
type MultiExecutor struct {
List []Executor
}
// Exec executes multiple process and gets single fastest result.
func (m MultiExecutor) Exec() (interface{}, error) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
ch := make(chan ExecResult)
for _, e := range m.List {
go func(e Executor) {
dataCh := make(chan ExecResult)
go func() {
dataCh <- doExec(e)
}()
select {
case <-ctx.Done():
// other slow process will be cancelled.
return
case data := <-dataCh:
// the fastest result
ch <- data
return
}
}(e)
}
data := <-ch // wait and get fastest result
return data.ImportantData, data.Err
}
// doExec executes process and gets result.
func doExec(e Executor) ExecResult {
result, err := e.Exec()
return ExecResult{
ImportantData: result,
Err: err,
}
}
package example
import (
"fmt"
"testing"
"time"
)
type ProcessWithWait struct {
Name string
Wait time.Duration
}
func (p ProcessWithWait) Exec() (interface{}, error) {
fmt.Printf("[process: %s] waiting...\n", p.Name)
time.Sleep(p.Wait)
fmt.Printf("[process: %s] end\n", p.Name)
return p.Name, nil
}
func TestProcess(t *testing.T) {
p1 := ProcessWithWait{
Name: "5sec",
Wait: 5 * time.Second,
}
p2 := ProcessWithWait{
Name: "3sec",
Wait: 3 * time.Second,
}
p3 := ProcessWithWait{
Name: "8sec",
Wait: 8 * time.Second,
}
m := &MultiExecutor{
List: []Executor{p1, p2, p3},
}
result, err := m.Exec()
if err != nil {
t.Errorf("[m.Exec()] expected err must be nil: actual=[%s]", err.Error())
return
}
if result != p2.Name {
t.Errorf("[m.Exec()] expected result must be [%s]: actual=[%s]", p2.Name, result)
}
time.Sleep(10 * time.Second)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment