Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement goroutines state monitor? #85

Open
PROger4ever opened this issue Feb 6, 2023 · 3 comments
Open

Implement goroutines state monitor? #85

PROger4ever opened this issue Feb 6, 2023 · 3 comments

Comments

@PROger4ever
Copy link

PROger4ever commented Feb 6, 2023

Sometimes there are cases, when goroutines states should be printed in console.
Similar functionality: hmdsefi/gowl

Usage proposal:

p := pool.New().WithMaxGoroutines(10).WithMonitor(monitor, time.Second)
p.Go(func(tp *threadParameter) (res interface{}, err error) {
    tp.setState("Doing work1...")
    err = work1()

    tp.setState("Doing work2...")
    res, err = work2()
    return
})

func monitor(ts []*threadState) error {
    return fmt.Fprintf(os.Stderr, "Thread states: %v", ts);
}
@PROger4ever
Copy link
Author

PROger4ever commented Feb 7, 2023

Also, a job can be a struct with method Go() instead of func() (or even an interface with default struct-implementation). It makes us able to send any data to a goroutine easy

@camdencheek
Copy link
Member

camdencheek commented Feb 7, 2023

Hi @PROger4ever! Thanks for the idea

I'm not sure it would be possible to design this in a way that is general enough to be broadly useful and without adding significant surface area to the the API of the library. For example, what if you need to know something other than a string? Or what if you don't want to monitor ever second, but instead you want to print to stderr every time a SIGHUP is sent?

I think this can be implemented more generally outside of the library in a way that doesn't significantly constrain a user's choice of how to monitor. The monitoring hooks can be passed in to the closure, then control is left in the hands of the user.

func main() {
	p := pool.New()
	states := make([]atomic.Value, 10)
	for i := 0; i < 10; i++ {
		i := i
		p.Go(func() {
			states[i].Store("Doing work 1...")
			time.Sleep(time.Second)
			states[i].Store("Doing work 2...")
			time.Sleep(time.Second)
		})
	}

	monitorCtx, cancelMonitor := context.WithCancel(context.Background())
	monitorPool := pool.New().WithContext(monitorCtx)
	monitorPool.Go(func(ctx context.Context) error {
		for {
			select {
			case <-ctx.Done():
				return nil
			case <-time.After(100 * time.Millisecond):
				for i := range states {
					fmt.Fprintf(os.Stderr, "%d: %s\n", i, states[i].Load())
				}
			}
		}
	})

	p.Wait()
	cancelMonitor()
	monitorPool.Wait()
}

@camdencheek
Copy link
Member

Also, a job can be a struct with method Go() instead of func() (or even an interface with default struct-implementation)

You can already pretty easily do this just by passing the struct's method to the pool.Go() call.

type myTask struct {
	val int
}

func (t myTask) Run() {
	println(t.val)
}

func main() {
	p := pool.New()
	for i := 0; i < 10; i++ {
		task := myTask{i}
		p.Go(task.Run)
	}
	p.Wait()
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants