两种不同方式的并发

71 阅读1分钟
package main

import (
	"fmt"
	"io"
	"log"
	"net/http"
	"sync"
	"time"
)

type result struct {
	value interface{}
	err   error
}

type Memo struct {
	requests chan request
}

type request struct {
	key      string
	response chan<- result
}

type entry struct {
	res   result
	ready chan struct{}
}

func httpGetBody(url string) (interface{}, error) {
	resp, err := http.Get(url)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	return io.ReadAll(resp.Body)
}

type Func func(key string) (interface{}, error)

func New(f Func) *Memo {
	memo := &Memo{requests: make(chan request)}
	go memo.server(f)
	return memo
}

func (memo *Memo) Get(key string) (value interface{}, err error) {
	response := make(chan result)
	memo.requests <- request{key, response}
	res := <-response
	return res.value, res.err
}

func (memo *Memo) server(f Func) {
	cache := make(map[string]*entry)
	for req := range memo.requests {
		e := cache[req.key]
		if e == nil {
			e = &entry{ready: make(chan struct{})}
			cache[req.key] = e
			go e.call(f, req.key)
		}
		go e.deliver(req.response)
	}
}

func (e *entry) call(f Func, key string) {
	e.res.value, e.res.err = f(key)
	close(e.ready)
}

func (e *entry) deliver(response chan<- result) {
	<-e.ready
	response <- e.res
}

func incomingURLs() <-chan string {
	ch := make(chan string)
	go func() {
		for _, url := range []string{
			"https://golang.org", "https://godoc.org",
			"https://play.golang.org", "http://gopl.io",
			"https://golang.org", "https://godoc.org",
			"https://play.golang.org", "http://gopl.io",
		} {
			ch <- url
		}
		close(ch)
	}()
	return ch
}

func main() {
	m := New(httpGetBody)
	var n sync.WaitGroup
	for url := range incomingURLs() {
		n.Add(1)
		go func(url string) {
			start := time.Now()
			_, err := m.Get(url)
			if err != nil {
				log.Print(err)
			}
			fmt.Printf("%s, %s \n",
				url, time.Since(start))
			n.Done()
		}(url)
	}
	n.Wait()
}
package main

import (
   "fmt"
   "io"
   "log"
   "net/http"
   "sync"
   "time"
)

type entry struct {
   res   result
   ready chan struct{}
}

func httpGetBody(url string) (interface{}, error) {
   resp, err := http.Get(url)
   if err != nil {
      return nil, err
   }
   defer resp.Body.Close()
   return io.ReadAll(resp.Body)
}

type Func func(key string) (interface{}, error)

type result struct {
   value interface{}
   err   error
}

type Memo struct {
   f     Func
   mu    sync.Mutex
   cache map[string]*entry
}

func New(f Func) *Memo {
   return &Memo{f: f, cache: make(map[string]*entry)}
}

func (memo *Memo) Get(key string) (value interface{}, err error) {
   memo.mu.Lock()
   e := memo.cache[key]
   if err == nil {
      e = &entry{ready: make(chan struct{})}
      memo.cache[key] = e
      memo.mu.Unlock()
      e.res.value, e.res.err = memo.f(key)
      close(e.ready)
   } else {
      memo.mu.Unlock()
      <-e.ready
   }
   return e.res.value, e.res.err
}

func incomingURLs() <-chan string {
   ch := make(chan string)
   go func() {
      for _, url := range []string{
         "https://golang.org", "https://godoc.org",
         "https://play.golang.org", "http://gopl.io",
         "https://golang.org", "https://godoc.org",
         "https://play.golang.org", "http://gopl.io",
      } {
         ch <- url
      }
      close(ch)
   }()
   return ch
}

func main() {
   m := New(httpGetBody)
   var n sync.WaitGroup
   for url := range incomingURLs() {
      n.Add(1)
      go func(url string) {
         start := time.Now()
         _, err := m.Get(url)
         if err != nil {
            log.Print(err)
         }
         fmt.Printf("%s, %s \n",
            url, time.Since(start))
         n.Done()
      }(url)
   }
   n.Wait()
}