Решение на Concurrent Retry Executor от Веселин Стоянов

Обратно към всички решения

Към профила на Веселин Стоянов

Резултати

  • 8 точки от тестове
  • 0 бонус точки
  • 8 точки общо
  • 7 успешни тест(а)
  • 2 неуспешни тест(а)

Код

package main
func ConcurrentRetryExecutor(tasks []func() string, concurrentLimit int, retryLimit int) <-chan struct {
index int
result string
} {
results_channel := make(chan struct {
index int
result string
})
tasks_channel := make(chan struct {
index int
function func() string
})
go func() {
workers := 0
for i := 0; i < concurrentLimit; i++ {
go func() {
workers++
for task := range tasks_channel {
retries := 0
done := false
for done == false && retries < retryLimit {
task_result := task.function()
result := struct {
index int
result string
}{
task.index,
task_result,
}
results_channel <- result
retries++
if task_result != "" {
done = true
}
}
}
if workers == 1 {
close(results_channel)
}
workers--

Този ред може да бъде изпълнен от няколко горутини едновременно със себе си/ ред 22 или 22 ред със себе си от няколко горутини, което би довело до race condition.

Опитай се да измислиш начин на решаване на проблема използваш канали а не int-ове

}()
}
}()
go func() {
for i, task := range tasks {
tasks_channel <- struct {
index int
function func() string
}{
index: i,
function: task,
}
}
close(tasks_channel)
}()
return results_channel
}

Лог от изпълнението

panic: close of closed channel

goroutine 12 [running]:
panic(0x4f6020, 0xc420010140)
	/usr/local/go/src/runtime/panic.go:500 +0x1a1
_/tmp/d20161115-21147-4t5nwa.ConcurrentRetryExecutor.func1.1(0xc420010130, 0xc42005c540, 0x5, 0xc42005c4e0)
	/tmp/d20161115-21147-4t5nwa/solution.go:46 +0x192
created by _/tmp/d20161115-21147-4t5nwa.ConcurrentRetryExecutor.func1
	/tmp/d20161115-21147-4t5nwa/solution.go:49 +0x8e
exit status 2
FAIL	_/tmp/d20161115-21147-4t5nwa	0.006s
PASS
ok  	_/tmp/d20161115-21147-4t5nwa	0.024s
PASS
ok  	_/tmp/d20161115-21147-4t5nwa	0.021s
PASS
ok  	_/tmp/d20161115-21147-4t5nwa	0.003s
PASS
ok  	_/tmp/d20161115-21147-4t5nwa	0.003s
PASS
ok  	_/tmp/d20161115-21147-4t5nwa	0.060s
panic: test timed out after 1s

goroutine 1033 [running]:
panic(0x4ec2e0, 0xc420010660)
	/usr/local/go/src/runtime/panic.go:500 +0x1a1
testing.startAlarm.func1()
	/usr/local/go/src/testing/testing.go:918 +0x10b
created by time.goFunc
	/usr/local/go/src/time/sleep.go:154 +0x44

goroutine 1 [chan receive]:
testing.(*T).Run(0xc4200720c0, 0x5164b8, 0xf, 0x524300, 0xc42003bd01)
	/usr/local/go/src/testing/testing.go:647 +0x316
testing.RunTests.func1(0xc4200720c0)
	/usr/local/go/src/testing/testing.go:793 +0x6d
testing.tRunner(0xc4200720c0, 0xc42003be20)
	/usr/local/go/src/testing/testing.go:610 +0x81
testing.RunTests(0x524328, 0x5a0320, 0x9, 0x9, 0x516086)
	/usr/local/go/src/testing/testing.go:799 +0x2f5
testing.(*M).Run(0xc42003bee8, 0xc420010510)
	/usr/local/go/src/testing/testing.go:743 +0x85
main.main()
	_/tmp/d20161115-21147-4t5nwa/_test/_testmain.go:72 +0xc6

goroutine 6 [chan receive]:
_/tmp/d20161115-21147-4t5nwa.TestPerformance(0xc420072180)
	/tmp/d20161115-21147-4t5nwa/solution_test.go:197 +0x1dd
testing.tRunner(0xc420072180, 0x524300)
	/usr/local/go/src/testing/testing.go:610 +0x81
created by testing.(*T).Run
	/usr/local/go/src/testing/testing.go:646 +0x2ec
exit status 2
FAIL	_/tmp/d20161115-21147-4t5nwa	1.005s
PASS
ok  	_/tmp/d20161115-21147-4t5nwa	0.216s
PASS
ok  	_/tmp/d20161115-21147-4t5nwa	0.203s

История (2 версии и 8 коментара)

Веселин обнови решението на 13.11.2016 16:23 (преди над 1 година)

+package main
+
+func main() {
+
+}
+
+type channelResult struct {
+ index int
+ result string
+}
+
+func ConcurrentRetryExecutor(tasks []func() string, concurrentLimit int, retryLimit int) <-chan channelResult {
+ ch := make(chan channelResult)
+
+ process := func() {
+ workers := 0
+ tasks_cap := cap(tasks)
+
+ for i := 0; i < tasks_cap; {
+ if workers < concurrentLimit {
+ workers++
+ go executeTask(tasks[i], i, tasks_cap, ch, retryLimit, 1, &workers, false)
+ i++
+ }
+ }
+ }
+
+ go process()
+
+ return ch
+}
+
+func executeTask(f func() string, index int, tasks_cap int, ch chan channelResult, retryLimit int, retries int, workers *int, skip bool) {
+ function_result := f()
+ result := channelResult{index: index, result: function_result}
+
+ ch <- result
+
+ if function_result == "" && retries < retryLimit {
+ executeTask(f, index, tasks_cap, ch, retryLimit, retries+1, workers, true)
+ } else {
+ *workers--
+
+ if *workers == 0 {
+ close(ch)
+ }
+ }
+}
  • всичко което правиш с workers е типичен пример за race condition който даже даваме почти директно във лекцията.
  • част от параметрите в executeTask изглежда само се препращат но не се използват
  • busy loop-а трябва да изчезне - както съм писал има елегантен и прост начин преподаден на лекцията с който да не се налага да се върти и да хаби процесорно време

Веселин обнови решението на 15.11.2016 01:40 (преди над 1 година)

package main
-func main() {
-
-}
-
-type channelResult struct {
+func ConcurrentRetryExecutor(tasks []func() string, concurrentLimit int, retryLimit int) <-chan struct {
index int
result string
-}
+} {
+ results_channel := make(chan struct {
+ index int
+ result string
+ })
-func ConcurrentRetryExecutor(tasks []func() string, concurrentLimit int, retryLimit int) <-chan channelResult {
- ch := make(chan channelResult)
+ tasks_channel := make(chan struct {
+ index int
+ function func() string
+ })
- process := func() {
+ go func() {
workers := 0
- tasks_cap := cap(tasks)
- for i := 0; i < tasks_cap; {
- if workers < concurrentLimit {
+ for i := 0; i < concurrentLimit; i++ {
+ go func() {
workers++
- go executeTask(tasks[i], i, tasks_cap, ch, retryLimit, 1, &workers, false)
- i++
- }
- }
- }
+ for task := range tasks_channel {
+ retries := 0
+ done := false
- go process()
+ for done == false && retries < retryLimit {
+ task_result := task.function()
+ result := struct {
+ index int
+ result string
+ }{
+ task.index,
+ task_result,
+ }
- return ch
-}
+ results_channel <- result
+ retries++
+ if task_result != "" {
+ done = true
+ }
+ }
+ }
-func executeTask(f func() string, index int, tasks_cap int, ch chan channelResult, retryLimit int, retries int, workers *int, skip bool) {
- function_result := f()
- result := channelResult{index: index, result: function_result}
+ if workers == 1 {
+ close(results_channel)
+ }
+ workers--

Този ред може да бъде изпълнен от няколко горутини едновременно със себе си/ ред 22 или 22 ред със себе си от няколко горутини, което би довело до race condition.

Опитай се да измислиш начин на решаване на проблема използваш канали а не int-ове

+ }()
+ }
+ }()
- ch <- result
-
- if function_result == "" && retries < retryLimit {
- executeTask(f, index, tasks_cap, ch, retryLimit, retries+1, workers, true)
- } else {
- *workers--
-
- if *workers == 0 {
- close(ch)
+ go func() {
+ for i, task := range tasks {
+ tasks_channel <- struct {
+ index int
+ function func() string
+ }{
+ index: i,
+ function: task,
+ }
}
- }
+ close(tasks_channel)
+ }()
+
+ return results_channel
}