Go:通过渠道传递功能

tyu*_*980 7 concurrency go rate-limiting

我试图通过将它们放在队列中以便稍后访问来限制我调用的函数.下面我有一些我创建的请求,requestHandler函数以一定的速率处理每个请求.

我希望它接受具有不同类型参数的各种函数,因此接口{}类型.

我如何通过频道传递功能并成功调用它们?

type request struct {
    function interface{}
    channel  chan interface{}
}

var requestQueue []request

func pushQueue(f interface{}, ch chan interface{}) {
    req := request{
        f,
        ch,
    }

    //push
    requestQueue = append(requestQueue, req)
}

func requestHandler() {
    for {
        if len(requestQueue) > 0 {
            //pop
            req := requestQueue[len(requestQueue)-1]
            requestQueue = requestQueue[:len(requestQueue)-1]

            req.channel <- req.function
        }

        <-time.After(1200 * time.Millisecond)
    }
}
Run Code Online (Sandbox Code Playgroud)

下面是我想要实现(GetLeagueEntries(字符串,字符串)和GetSummonerName(INT,INT)是函数)的例子:

ch := make(chan interface{})
    pushQueue(l.GetLeagueEntries, ch)
    pushQueue(l.GetSummonerName, ch)

    leagues, _ := <-ch(string1, string2)
    summoners, _ := <-ch(int1, int2)
Run Code Online (Sandbox Code Playgroud)

小智 2

好的,这是代码:https ://play.golang.org/p/XZvb_4BaJF

请注意,它并不完美。您有一个每秒执行一次的队列。如果队列为空并且添加了新项目,则新项目在执行之前可能会等待几乎一秒钟。

但这应该能让你非常接近你所需要的:)

这段代码可以分为 3 部分:

  1. 速率限制队列执行器,我称之为服务器(我不擅长命名事物) - 服务器对功能一无所知。它所做的就是启动一个永无止境的 goroutine,每秒弹出队列中最旧的函数并调用它。我上面谈到的问题就在代码的这一部分中,顺便说一句,如果您愿意,我可以帮助您解决它。
  2. 按钮单击功能 - 这向您展示了每次按钮单击如何使用服务器调用 3 个 diff 函数(您显然可以进行更多/更少的函数调用),并确保它们彼此间隔 1 秒。您甚至可以为任何函数添加超时(以伪造延迟),它们仍然会间隔 1 秒被调用。这是唯一需要通道的地方,因为您希望尽可能快地调用所有函数(如果第一个函数需要 5 秒,您只想等待 1 秒来调用第二个函数),然后等待它们调用完成所以你需要知道它们什么时候完成。
  3. 按钮点击模拟(主要功能)——这只是表明 3 次按钮点击将按预期工作。你也可以把它们放在一个 goroutine 中来模拟 3 个用户同时点击按钮,它仍然可以工作。

    package main
    
    import (
        "fmt"
        "sync"
        "time"
    )
    
    const (
        requestFreq = time.Second
    )
    
    type (
        // A single request
        request func()
    
        // The server that will hold a queue of requests and make them once a requestFreq
        server struct {
            // This will tick once per requestFreq
            ticker     *time.Ticker
    
            requests []request
            // Mutex for working with the request slice
            sync.RWMutex
        }
    )
    
    var (
        createServerOnce sync.Once
        s *server
    )
    
    func main() {
        // Multiple button clicks:
        ButtonClick()
        ButtonClick()
        ButtonClick()
    
        fmt.Println("Done!")
    }
    
    
    
    
    
    
    // BUTTON LOGIC:
    
    // Calls 3 functions and returns 3 diff values.
    // Each function is called at least 1 second appart.
    func ButtonClick() (val1 int, val2 string, val3 bool) {
        iCh := make(chan int)
        sCh := make(chan string)
        bCh := make(chan bool)
    
        go func(){
            Server().AppendRequest(func() {
                t := time.Now()
                fmt.Println("Calling func1 (time: " + t.Format("15:04:05") + ")")
                // do some stuff
                iCh <- 1
            })
        }()
        go func(){
            Server().AppendRequest(func() {
                t := time.Now()
                fmt.Println("Calling func2 (time: " + t.Format("15:04:05") + ")")
                // do some stuff
                sCh <- "Yo"
            })
        }()
        go func(){
            Server().AppendRequest(func() {
                t := time.Now()
                fmt.Println("Calling func3 (time: " + t.Format("15:04:05") + ")")
                // do some stuff
                bCh <- true
            })
        }()
    
        // Wait for all 3 calls to come back
        for count := 0; count < 3; count++ {
            select {
            case val1 = <-iCh:
            case val2 = <-sCh:
            case val3 = <-bCh:
            }
        }
    
        return
    }
    
    
    
    
    
    // SERVER LOGIC
    
    // Factory function that will only create a single server
    func Server() *server {
        // Only one server for the entire application
        createServerOnce.Do(func() {
            s = &server{ticker: time.NewTicker(requestFreq), requests: []request{}}
    
            // Start a thread to make requests.
            go s.makeRequests()
        })
        return s
    }
    func (s *server) makeRequests() {
        if s == nil || s.ticker == nil {
            return
        }
    
        // This will keep going once per each requestFreq
        for _ = range s.ticker.C {
    
            var r request
    
            // You can't just access s.requests because you are in a goroutine
            // here while someone could be adding new requests outside of the 
            // goroutine so you have to use locks.
            s.Lock()
            if len(s.requests) > 0 {
                // We have a lock here, which blocks all other operations 
                // so just shift the first request out, save it and give 
                // the lock back before doing any work.
                r = s.requests[0]
                s.requests = s.requests[1:]
            }
            s.Unlock()
    
            if r != nil {
                // make the request!
                r()
            }
        }
    }
    func (s *server) AppendRequest(r request) {
        if s == nil {
            return
        }
        s.Lock()
        s.requests = append(s.requests, r)
        s.Unlock()
    }
    
    Run Code Online (Sandbox Code Playgroud)