divid and conquer finished

This commit is contained in:
Jian Han 2018-01-06 13:53:28 +10:00
parent 69d47635a9
commit 821521fb7d
2 changed files with 74 additions and 20 deletions

View File

@ -1,6 +1,13 @@
package main
import "github.com/davecgh/go-spew/spew"
import (
"fmt"
"reflect"
"runtime"
"time"
"github.com/davecgh/go-spew/spew"
)
// https://medium.com/capital-one-developers/buffered-channels-in-go-what-are-they-good-for-43703871828
@ -11,36 +18,83 @@ import "github.com/davecgh/go-spew/spew"
// ideal way to gather the data back from your subtasks.
func main() {
evaluators := []Evaluator{google, yahoo, bing}
data := "Query"
r, e := DivideAndConquer(data, evaluators)
spew.Dump(r, e)
type in struct {
a int
b int
}
var google = func(data interface{}) (interface{}, error) {
return data, nil
type out struct {
source string
result int
}
var yahoo = func(data interface{}) (interface{}, error) {
return data, nil
evaluators := []Evaluator{
EvaluatorFunc(func(inV interface{}) (interface{}, error) {
i := inV.(in)
r := i.a + i.b
return out{"Plus", r}, nil
}),
EvaluatorFunc(func(inV interface{}) (interface{}, error) {
i := inV.(in)
r := i.a * i.b
return out{"Multi", r}, nil
}),
EvaluatorFunc(func(inV interface{}) (interface{}, error) {
i := inV.(in)
r := i.a - i.b
return out{"min", r}, nil
}),
EvaluatorFunc(func(inV interface{}) (interface{}, error) {
i := inV.(in)
r := i.a / i.b
return out{"divider", r}, nil
}),
}
var bing = func(data interface{}) (interface{}, error) {
return data, nil
r, errors := DivideAndConquer(in{2, 3}, evaluators, 10*time.Millisecond)
spew.Dump(r, errors)
}
type Evaluator func(data interface{}) (interface{}, error)
type Evaluator interface {
Evaluate(data interface{}) (interface{}, error)
Name() string
}
type EvaluatorFunc func(interface{}) (interface{}, error)
func DivideAndConquer(data interface{}, evaluators []Evaluator) ([]interface{}, []error) {
func (ef EvaluatorFunc) Evaluate(in interface{}) (interface{}, error) {
return ef(in)
}
func (ef EvaluatorFunc) Name() string {
return runtime.FuncForPC(reflect.ValueOf(ef).Pointer()).Name()
}
func DivideAndConquer(data interface{}, evaluators []Evaluator, timeout time.Duration) ([]interface{}, []error) {
gather := make(chan interface{}, len(evaluators))
errors := make(chan error, len(evaluators))
for _, v := range evaluators {
go func(e Evaluator) {
result, err := e(data)
// Why not just use an unbuffered channel? The answer is that we dont want to leak any goroutines.
// While the Go runtime is capable of handling thousands or hundreds of thousands of goroutines at a time,
// each goroutine does use some resources, so you dont want to leave them hanging around when
// you dont have to. If you do, a long-running Go program will start performing poorly
ch := make(chan interface{}, 1)
ech := make(chan error, 1)
go func() {
result, err := e.Evaluate(data)
if err != nil {
errors <- err
} else {
gather <- result
ch <- result
}
}()
select {
case r := <-ch:
gather <- r
case err := <-ech:
errors <- err
case <-time.After(timeout):
errors <- fmt.Errorf("%s timeout after %v on %v", e.Name(), timeout, data)
}
}(v)
}

BIN
concurrency/subtasks/subtasks Executable file

Binary file not shown.