I've been trying to create a simple event loop wrapper in Go. But I got stumped, how was I supposed to keep track of operations in the current thread? I wanted CurrentTick to run a function, and even if the calling function quits, not start the next tick until all functions run by CurrentTick quit. I thought I might use a mutex to monitor the number of threads, but I realized if I kept checking that over and over it would throttle the CPU. If I used time.Sleep it would be latent. How would you solve the problem?
package eventloop
import (
"reflect"
)
type eventLoop *struct{
functions []reflect.Value
addFunc chan<-/*3*/ reflect.Value
mutex chan/*1*/ bool
threads int
}
func NewEventLoop() eventLoop {
var funcs chan reflect.Value
loop := eventLoop{
[]Reflect.Value{},
funcs = make(chan reflect.Value, 3),
make(chan bool, 1),
0,
}
go func(){
for {
this.mutex <- 1
if threads == 0 {
}
}
开发者_C百科 }
}
func (this eventLoop) NextTick(f func()) {
this.addFunc <- reflect.ValueOf(f)
}
func (this eventLoop) CurrentTick(f func()) {
this.mutex <- 1
threads += 1
<-this.mutex
go func() {
f()
this.mutex <- 1
threads -= 1
<-this.mutex
}()
}
If I understand your intent, I think you're overcomplicating things. I'd do it like this:
package eventloop
type EventLoop struct {
nextFunc chan func()
curFunc chan func()
}
func NewEventLoop() *EventLoop {
el := &EventLoop{
// Adjust the capacities to taste
make(chan func(), 3),
make(chan func(), 3),
}
go eventLoop(el)
return el
}
func (el *EventLoop) NextTick(f func()) {
el.nextFunc <- f
}
func (el *EventLoop) CurrentTick(f func()) {
el.curFunc <- f
}
func (el *EventLoop) Quit() {
close(el.nextFunc)
}
func eventLoop(el *EventLoop) {
for {
f, ok := <-el.nextFunc
if !ok {
return
}
f()
drain: for {
select {
case f := <-el.curFunc:
f()
default:
break drain
}
}
}
}
Depending on your use, you may need to add some synchronization to make sure all tasks in the loop finish before your program exits.
I figured it out myself, after a lot of problems and random issues including using 15 as length instead of capacity... Seems you just have a thread send a message after you decrement the counter. (the loop.tick part could be inlined, but I'm not worried about that)
package eventloop
type eventLoop struct{
functions []func()
addFunc chan/*3*/ func()
mutex chan/*1*/ bool
threads int
waitChannel chan bool
pauseState chan bool
}
func (this *eventLoop) NextTick (f func()) {
this.addFunc <- f
}
func (this *eventLoop) tick () {
this.mutex <- true
for this.threads != 0 {
<-this.mutex
<-this.waitChannel
this.mutex <- true
}
<-this.mutex
L1: for {
select {
case f := <-this.addFunc:
this.functions = append(this.functions,f)
default: break L1
}
}
if len(this.functions) != 0 {
this.functions[0]()
if len(this.functions) >= 2 {
this.functions = this.functions[1:]
} else {
this.functions = []func(){}
}
} else {
(<-this.addFunc)()
}
}
func (this *eventLoop) CurrentTick (f func()) {
this.mutex <- true
this.threads += 1
<-this.mutex
go func() {
f()
this.mutex <- true
this.threads -= 1
<-this.mutex
this.waitChannel <- true
}()
}
func NewEventLoop () *eventLoop {
funcs := make(chan func(),3)
loop := &eventLoop{
make([]func(),0,15), /*functions*/
funcs, /*addFunc*/
make(chan bool, 1), /*mutex for threads*/
0, /*Number of threads*/
make(chan bool,0), /*The "wait" channel*/
make(chan bool,1),
}
go func(){
for { loop.tick() }
}()
return loop
}
Note: this still has lots of other problems.
精彩评论