Решение на Concurrent Retry Executor от Екатерина Горанова

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

Към профила на Екатерина Горанова

Резултати

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

Код

package main
import "strconv"
type ChanList struct {
channel chan string
next *ChanList
hasNext chan bool
}
func (a *ChanList) readInto(out chan string, n int) {
for msg := range a.channel {
out <- strconv.Itoa(n) + "\t" + msg
}
if <-a.hasNext {
a.next.readInto(out, n+1)
} else {
close(out)
}
}
func OrderedLogDrainer(logs chan (chan string)) chan string {
result := make(chan string)
go func() {
prevBuffered := &ChanList{}
for log := range logs {
channel := make(chan string, 100)
hasNext := make(chan bool, 1)
buffered := &ChanList{channel: channel, hasNext: hasNext}
if prevBuffered.channel == nil {
go buffered.readInto(result, 1)
} else {
prevBuffered.next = buffered
prevBuffered.hasNext <- true
}
prevBuffered = buffered
log := log
go func() {
for msg := range log {
buffered.channel <- msg
}
close(buffered.channel)
}()
}
prevBuffered.hasNext <- false
}()
return result
}

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

PASS
ok  	_/tmp/d20151110-19113-yt6n4p	0.003s
PASS
ok  	_/tmp/d20151110-19113-yt6n4p	0.003s
PASS
ok  	_/tmp/d20151110-19113-yt6n4p	0.003s
--- FAIL: TestWithNoLogs (0.50s)
	solution_test.go:27: Test exceeded allowed time of 500 milliseconds
FAIL
exit status 1
FAIL	_/tmp/d20151110-19113-yt6n4p	0.503s
PASS
ok  	_/tmp/d20151110-19113-yt6n4p	0.003s
PASS
ok  	_/tmp/d20151110-19113-yt6n4p	0.703s
PASS
ok  	_/tmp/d20151110-19113-yt6n4p	0.004s
PASS
ok  	_/tmp/d20151110-19113-yt6n4p	0.005s

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

Екатерина обнови решението на 07.11.2015 18:38 (преди над 2 години)

+package main
+
+import "strconv"
+
+func OrderedLogDrainer(logs chan (chan string)) chan string {
+ result := make(chan string)
+ bufchans := make(chan (chan string), 100)
+
+ go func() {
+ for log := range logs {
+ log := log
+ buffered := make(chan string, 100)
+ go func() {
+ for s := range log {
+ buffered <- s
+ }
+ close(buffered)
+ }()
+ bufchans <- buffered
+ }
+ close(bufchans)
+ }()
+
+ go func() {
+ var ind int
+ for bc := range bufchans {
+ ind++
+ for s := range bc {
+ result <- strconv.Itoa(ind) + "\t" + s
+ }
+ }
+ close(result)
+ }()
+
+ return result
+}

:thumbsup: супер кратко и ясно решение. Виждам само един потенциален проблем: в условието сме написали, че по всеки лог ще бъдат пратени максимум 100 съобщения, но никъде не сме казали, че ще има под 100 log-а :)

Екатерина обнови решението на 08.11.2015 23:11 (преди над 2 години)

package main
-import "strconv"
+import (
+ "strconv"
+ "sync"
+)
func OrderedLogDrainer(logs chan (chan string)) chan string {
result := make(chan string)
- bufchans := make(chan (chan string), 100)
+ var bufchans []chan string
+ logsClosed := false
+ var m = &sync.Mutex{}
+
go func() {
for log := range logs {
log := log
+
buffered := make(chan string, 100)
+ m.Lock()
+ bufchans = append(bufchans, buffered)
+ m.Unlock()
+
go func() {
- for s := range log {
- buffered <- s
+ for msg := range log {
+ buffered <- msg
}
close(buffered)
}()
- bufchans <- buffered
}
- close(bufchans)
+ logsClosed = true
}()
go func() {
- var ind int
- for bc := range bufchans {
- ind++
- for s := range bc {
- result <- strconv.Itoa(ind) + "\t" + s
+ var index int
+ for !logsClosed || len(bufchans) > 0 {
+ if len(bufchans) > 0 {
+ m.Lock()
+ bufchan := bufchans[0]
+ bufchans = bufchans[1:]
+ m.Unlock()
+
+ index++
+ for msg := range bufchan {
+ result <- strconv.Itoa(index) + "\t" + msg
+ }
}
}
close(result)
}()
return result
}

Помисли за решение, в което няма нужда от mutex-и. В момента имаш потенциален race condition, защото редове 36-37 четат големината на bufchans, но mutex-а не е заключен.

btw Go си има вграден race condition detector. Можеш да ползваш тестовете на Даниел (или твои собствени) и да ги пуснеш с go test -race ./..., за да провериш за подобни проблеми.

Екатерина обнови решението на 10.11.2015 11:50 (преди над 2 години)

package main
-import (
- "strconv"
- "sync"
-)
+import "strconv"
+type ChanList struct {
+ channel chan string
+ next *ChanList
+ hasNext chan bool
+}
+
+func (a *ChanList) readInto(out chan string, n int) {
+ for msg := range a.channel {
+ out <- strconv.Itoa(n) + "\t" + msg
+ }
+
+ if <-a.hasNext {
+ a.next.readInto(out, n+1)
+ } else {
+ close(out)
+ }
+}
+
func OrderedLogDrainer(logs chan (chan string)) chan string {
result := make(chan string)
- var bufchans []chan string
- logsClosed := false
- var m = &sync.Mutex{}
-
go func() {
+ prevBuffered := &ChanList{}
+
for log := range logs {
- log := log
+ channel := make(chan string, 100)
+ hasNext := make(chan bool, 1)
- buffered := make(chan string, 100)
- m.Lock()
- bufchans = append(bufchans, buffered)
- m.Unlock()
+ buffered := &ChanList{channel: channel, hasNext: hasNext}
+ if prevBuffered.channel == nil {
+ go buffered.readInto(result, 1)
+ } else {
+ prevBuffered.next = buffered
+ prevBuffered.hasNext <- true
+ }
+ prevBuffered = buffered
+ log := log
go func() {
for msg := range log {
- buffered <- msg
+ buffered.channel <- msg
}
- close(buffered)
+ close(buffered.channel)
}()
}
- logsClosed = true
- }()
- go func() {
- var index int
- for !logsClosed || len(bufchans) > 0 {
- if len(bufchans) > 0 {
- m.Lock()
- bufchan := bufchans[0]
- bufchans = bufchans[1:]
- m.Unlock()
-
- index++
- for msg := range bufchan {
- result <- strconv.Itoa(index) + "\t" + msg
- }
- }
- }
- close(result)
+ prevBuffered.hasNext <- false
}()
return result
-}
+}