반응형
SMALL
미래(Future)
미래 디자인 패턴은 결국 동일한 고루틴 또는 다른 고루틴에 의해 실행될 알고리즘을 작성할 수 있게 해줍니다.
package future
type SuccessFunc func(string)
type FailFunc func(error)
type ExecuteStringFunc func() (string, error)
type MaybeString struct {
successFunc SuccessFunc
failFunc FailFunc
}
func (s *MaybeString) Success(f SuccessFunc) *MaybeString {
s.successFunc = f
return s
}
func (s *MaybeString) Fail(f FailFunc) *MaybeString {
s.failFunc = f
return s
}
func (s *MaybeString) Execute(f ExecuteStringFunc) {
go func(s *MaybeString) {
str, err := f()
if err != nil {
s.failFunc(err)
} else {
s.successFunc(str)
}
}(s)
}
Test Code
package future
import (
"errors"
"fmt"
"sync"
"testing"
"time"
)
func TestStringOrError_Execute(t *testing.T) {
future := &MaybeString{}
t.Run("Success result", func(t *testing.T) {
var wg sync.WaitGroup
wg.Add(1)
//Timeout!
go timeout(t, &wg)
future.Success(func(s string) {
t.Log(s)
wg.Done()
}).Fail(func(e error) {
t.Fail()
wg.Done()
})
future.Execute(func() (string, error) {
return "Hello World!", nil
})
wg.Wait()
})
t.Run("Error result", func(t *testing.T) {
var wg sync.WaitGroup
wg.Add(1)
future.Success(func(s string) {
t.Fail()
wg.Done()
}).Fail(func(e error) {
t.Log(e.Error())
wg.Done()
})
future.Execute(func() (string, error) {
return "", errors.New("Error ocurred")
})
wg.Wait()
})
t.Run("Closure Success result", func(t *testing.T) {
var wg sync.WaitGroup
wg.Add(1)
//Timeout!
go timeout(t, &wg)
future.Success(func(s string) {
t.Log(s)
wg.Done()
}).Fail(func(e error) {
t.Fail()
wg.Done()
})
future.Execute(setContext("Hello"))
wg.Wait()
})
}
func setContext(msg string) ExecuteStringFunc {
msg = fmt.Sprintf("%s Closure!\n", msg)
return func() (string, error) {
return msg, nil
}
}
func timeout(t *testing.T, wg *sync.WaitGroup) {
time.Sleep(time.Second)
t.Log("Timeout!")
t.Fail()
wg.Done()
}
파이프라인(Pipeline)
파이프라인 디자인 패턴은 어떤 논리에 따라 서로 연결된 고루틴의 복잡한 동기 흐름을 구축하는 강력한 패턴입니다.
package pipeline
func LaunchPipeline(amount int) int {
return <-sum(power(generator(amount)))
}
func generator(max int) <-chan int {
outChInt := make(chan int, 100)
go func() {
for i := 1; i <= max; i++ {
outChInt <- i
}
close(outChInt)
}()
return outChInt
}
func power(in <-chan int) <-chan int {
out := make(chan int, 100)
go func() {
for v := range in {
out <- v * v
}
close(out)
}()
return out
}
func sum(in <-chan int) <-chan int {
out := make(chan int, 100)
go func() {
var sum int
for v := range in {
sum += v
}
out <- sum
close(out)
}()
return out
}
Test Code
package pipeline
import "testing"
func TestLaunchPipeline(t *testing.T) {
tableTest := [][]int{
{3, 14},
{5, 55}}
var res int
for _, test := range tableTest {
res = LaunchPipeline(test[0])
if res != test[1] {
t.Fatal()
}
t.Logf("%d == %d\n", res, test[1])
}
}
워커 풀(Worker Pool)
동시성에 대한 이전의 접근 방식 중 일부와 관련하여 우리가 직면할 수 있는 문제 중 하나는 무한한 컨텍스트입니다. 우리는 앱이 무제한의 고루틴을 만들게 할 수 없습니다.고루틴은 가볍지만, 고루틴이 하는 일은 매우 무거울 수 있으므로 앱이 무제한의 고루틴을 만들게 할 수 없고, 워커 풀 디자인 패턴은 이 문제를 해결하는 데 도움이 됩니다.
package main
import "time"
type Dispatcher interface {
LaunchWorker(w WorkerLauncher)
MakeRequest(Request)
Stop()
}
type dispatcher struct {
inCh chan Request
}
func (d *dispatcher) LaunchWorker(w WorkerLauncher) {
w.LaunchWorker(d.inCh)
}
func (d *dispatcher) Stop() {
close(d.inCh)
}
func (d *dispatcher) MakeRequest(r Request) {
select {
case d.inCh <- r:
case <-time.After(time.Second * 5):
return
}
}
func NewDispatcher(b int) Dispatcher {
return &dispatcher{
inCh: make(chan Request, b),
}
}
package main
import (
"fmt"
"strings"
)
type WorkerLauncher interface {
LaunchWorker(in chan Request)
}
type PrefixSuffixWorker struct {
id int
prefixS string
suffixS string
}
func (w *PrefixSuffixWorker) LaunchWorker(in chan Request) {
w.prefix(w.append(w.uppercase(in)))
}
func (w *PrefixSuffixWorker) uppercase(in <-chan Request) <-chan Request {
out := make(chan Request)
go func() {
for msg := range in {
s, ok := msg.Data.(string)
if !ok {
msg.Handler(nil)
continue
}
msg.Data = strings.ToUpper(s)
out <- msg
}
close(out)
}()
return out
}
func (w *PrefixSuffixWorker) append(in <-chan Request) <-chan Request {
out := make(chan Request)
go func() {
for msg := range in {
uppercaseString, ok := msg.Data.(string)
if !ok {
msg.Handler(nil)
continue
}
msg.Data = fmt.Sprintf("%s%s", uppercaseString, w.suffixS)
out <- msg
}
close(out)
}()
return out
}
func (w *PrefixSuffixWorker) prefix(in <-chan Request) {
go func() {
for msg := range in {
uppercasedStringWithSuffix, ok := msg.Data.(string)
if !ok {
msg.Handler(nil)
continue
}
msg.Handler(fmt.Sprintf("%s%s", w.prefixS, uppercasedStringWithSuffix))
}
}()
}
package main
import (
"fmt"
"log"
"sync"
)
type Request struct {
Data interface{}
Handler RequestHandler
}
type RequestHandler func(interface{})
func NewStringRequest(s string, wg *sync.WaitGroup) Request {
myRequest := Request{
Data: s,
Handler: func(i interface{}) {
defer wg.Done()
s, ok := i.(string)
if !ok {
log.Fatal("Invalid casting to string")
}
fmt.Println(s)
},
}
return myRequest
}
func main() {
bufferSize := 100
dispatcher := NewDispatcher(bufferSize)
workers := 3
for i := 0; i < workers; i++ {
w := &PrefixSuffixWorker{
prefixS: fmt.Sprintf("WorkerID: %d -> ", i),
suffixS: " World",
id: i,
}
dispatcher.LaunchWorker(w)
}
requests := 10
var wg sync.WaitGroup
wg.Add(requests)
for i := 0; i < requests; i++ {
req := NewStringRequest(fmt.Sprintf("(Msg_id: %d) -> Hello", i), &wg)
dispatcher.MakeRequest(req)
}
dispatcher.Stop()
wg.Wait()
}
반응형
LIST
'Go' 카테고리의 다른 글
[Algorithms] 지식 표현(Knowledge Representation) (0) | 2022.12.06 |
---|---|
[Algorithms] 탐색(Search) (0) | 2022.12.06 |
[Design Pattern] 중재인(Mediator), 관찰자(Observer), 장벽(Barrier) 패턴 (0) | 2022.11.30 |
[Design Pattern] 인터프리터(Interpreter), 방문자(Visitor), 상태(State) 패턴 (0) | 2022.11.30 |
[Design Pattern] 커맨드(Command), 템플릿(Template), 메멘토(Memento) 패턴 (0) | 2022.11.29 |