Last active
December 23, 2019 04:19
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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, | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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