github.com/robert-zaremba/parexec@v0.0.0-20150611124707-8929690d3378/worker_test.go (about)

     1  package parexec
     2  
     3  import (
     4  	"fmt"
     5  	"sync"
     6  	"testing"
     7  	"time"
     8  )
     9  
    10  func ExampleSimpleRun() {
    11  	total := 4
    12  	type Job struct {
    13  		Name     string
    14  		Duration int
    15  	}
    16  	jobs := make(chan *Job)
    17  	// use buffered channel to be able to fill all jobs and start getting results without blocking workers
    18  	results := make(chan string, total)
    19  	work := func(stop <-chan bool) {
    20  		for {
    21  			select {
    22  			case j := <-jobs:
    23  				time.Sleep(time.Duration(j.Duration))
    24  				results <- j.Name
    25  			case <-stop:
    26  				fmt.Println("closing")
    27  				return
    28  			}
    29  		}
    30  	}
    31  	done := func() {
    32  		close(jobs)
    33  	}
    34  	stop := SimpleRun(2, work, done)
    35  	// fill jobs
    36  	for i := 0; i < total; i++ {
    37  		jobs <- &Job{Name: fmt.Sprint(i), Duration: i * 1000}
    38  	}
    39  	// you can't use this: close(results); for r := range results
    40  	// because close(results) is not safe - there might be active goroutine
    41  	// which didn't get stop signal and tries to write to results
    42  	for i := 0; i < total; i++ {
    43  		r := <-results
    44  		fmt.Println(r)
    45  	}
    46  	close(stop)
    47  	// Wait for closing workers.
    48  	// Normally you should do it with separate channel (in a clojure, handled in `case <-stop:`) to get acknowledges
    49  	time.Sleep(200000)
    50  	// Output:
    51  	// 0
    52  	// 1
    53  	// 2
    54  	// 3
    55  	// closing
    56  	// closing
    57  }
    58  
    59  func ExampleSimpleRun_singleloop() {
    60  	var jobs = make(chan int)
    61  	var results = make(chan int)
    62  	work := func(stop <-chan bool) {
    63  		for {
    64  			select {
    65  			case j := <-jobs:
    66  				results <- j
    67  			case <-stop:
    68  				return
    69  			}
    70  		}
    71  	}
    72  	done := func() {
    73  		close(results)
    74  	}
    75  	stop := SimpleRun(2, work, done)
    76  	sum, r := 0, 0
    77  	for i := 0; i < TOTALBENCH; {
    78  		select {
    79  		case jobs <- i:
    80  			i++
    81  		case r = <-results:
    82  			sum += r
    83  		}
    84  	}
    85  	close(stop) // we need to manually stop to automatically stop results when all workers will finish
    86  	for r = range results {
    87  		sum += r
    88  	}
    89  }
    90  
    91  // ExampleSimpleRun_raw present the same computation as ExampleSimpleRun_singleloop
    92  // without using SimpleRun function and stop channel.
    93  func ExampleSimpleRun_raw() {
    94  	var jobs = make(chan int)
    95  	var results = make(chan int)
    96  	var wg sync.WaitGroup
    97  	for i := 0; i < 2; i++ {
    98  		wg.Add(1)
    99  		go func() {
   100  			for j := range jobs {
   101  				results <- j
   102  			}
   103  			wg.Done()
   104  		}()
   105  	}
   106  	go func() {
   107  		wg.Wait()
   108  		close(results)
   109  	}()
   110  	sum, r := 0, 0
   111  	for i := 0; i < TOTALBENCH; {
   112  		select {
   113  		case jobs <- i:
   114  			i++
   115  		case r = <-results:
   116  			sum += r
   117  		}
   118  	}
   119  	close(jobs) // we need to close jobs to stop workers and close results
   120  	for r = range results {
   121  		sum += r
   122  	}
   123  }
   124  
   125  func BenchmarkSimpleRun(*testing.B) {
   126  	ExampleSimpleRun_singleloop()
   127  }
   128  
   129  func BenchmarkSimpleRun_raw(*testing.B) {
   130  	ExampleSimpleRun_raw()
   131  }