猿问

Go:通过通道传递函数

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


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


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


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)

    }

}

这是我想要实现的示例(GetLeagueEntries(string, string) 和 GetSummonerName(int, int) 是函数):


ch := make(chan interface{})

    pushQueue(l.GetLeagueEntries, ch)

    pushQueue(l.GetSummonerName, ch)


    leagues, _ := <-ch(string1, string2)

    summoners, _ := <-ch(int1, int2)


拉丁的传说
浏览 208回答 3
3回答

侃侃无极

首先,我会把它写成:leagues := server.GetLeagueEntries()summoners := server.GetSummoners()并且,将速率限制放入服务器。使用速率限制库之一。但是,可以使用接口来统一请求,并使用 func 类型来允许闭包(如在 http.HandleFunc 中):type Command interface {&nbsp; &nbsp; Execute(server *Server)}type CommandFunc func(server *Server)func (fn CommandFunc) Execute(server *Server) { fn(server) }type GetLeagueEntries struct { Leagues []League }func (entries *GetLeagueEntries) Execute(server *Server) {&nbsp; &nbsp; // ...}func GetSummonerName(id int, result *string) CommandFunc {&nbsp; &nbsp; return CommandFunc(func(server *Server){&nbsp; &nbsp; &nbsp; &nbsp; *result = "hello"&nbsp; &nbsp; })}get := GetLeagueEnties{}requests <- &getrequests <- CommandFunc(func(server *Server){&nbsp; &nbsp; // ... handle struff here})当然,这需要一些同步。

当年话下

好的,这是代码:https : //play.golang.org/p/XZvb_4BaJF请注意,它并不完美。您有一个每秒执行一次的队列。如果队列为空并且添加了新项目,则新项目可以等待近一秒钟才能执行。但这应该让你非常接近你需要的东西:)此代码可以分为 3 部分:限速队列执行器,我称之为服务器(我很擅长命名)——服务器对这些功能一无所知。它所做的就是启动一个永无止境的 goroutine,每秒弹出一次队列中最旧的函数,并调用它。我上面谈到的问题在代码的这一部分 BTW,如果您愿意,我可以帮助您修复它。按钮单击功能 - 这向您展示了每次单击按钮如何使用服务器调用 3 个差异函数(您显然可以进行更多/更少的函数调用),并确保它们彼此相隔 1 秒。您甚至可以为任何函数添加超时(以伪造延迟),它们仍然会相隔 1 秒被调用。这是唯一需要通道的地方,因为您希望尽可能快地进行所有函数调用(如果第一个函数需要 5 秒,则您只想等待 1 秒来调用第二个函数)然后等待它们完成,所以你需要知道它们什么时候完成。按钮点击模拟(主要功能) - 这只是表明 3 次按钮点击会按预期工作。你也可以把它们放在一个 goroutine 中来模拟 3 个用户同时点击按钮,它仍然可以工作。package mainimport (&nbsp; &nbsp; "fmt"&nbsp; &nbsp; "sync"&nbsp; &nbsp; "time")const (&nbsp; &nbsp; requestFreq = time.Second)type (&nbsp; &nbsp; // A single request&nbsp; &nbsp; request func()&nbsp; &nbsp; // The server that will hold a queue of requests and make them once a requestFreq&nbsp; &nbsp; server struct {&nbsp; &nbsp; &nbsp; &nbsp; // This will tick once per requestFreq&nbsp; &nbsp; &nbsp; &nbsp; ticker&nbsp; &nbsp; &nbsp;*time.Ticker&nbsp; &nbsp; &nbsp; &nbsp; requests []request&nbsp; &nbsp; &nbsp; &nbsp; // Mutex for working with the request slice&nbsp; &nbsp; &nbsp; &nbsp; sync.RWMutex&nbsp; &nbsp; })var (&nbsp; &nbsp; createServerOnce sync.Once&nbsp; &nbsp; s *server)func main() {&nbsp; &nbsp; // Multiple button clicks:&nbsp; &nbsp; ButtonClick()&nbsp; &nbsp; ButtonClick()&nbsp; &nbsp; ButtonClick()&nbsp; &nbsp; 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) {&nbsp; &nbsp; iCh := make(chan int)&nbsp; &nbsp; sCh := make(chan string)&nbsp; &nbsp; bCh := make(chan bool)&nbsp; &nbsp; go func(){&nbsp; &nbsp; &nbsp; &nbsp; Server().AppendRequest(func() {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; t := time.Now()&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fmt.Println("Calling func1 (time: " + t.Format("15:04:05") + ")")&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // do some stuff&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; iCh <- 1&nbsp; &nbsp; &nbsp; &nbsp; })&nbsp; &nbsp; }()&nbsp; &nbsp; go func(){&nbsp; &nbsp; &nbsp; &nbsp; Server().AppendRequest(func() {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; t := time.Now()&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fmt.Println("Calling func2 (time: " + t.Format("15:04:05") + ")")&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // do some stuff&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; sCh <- "Yo"&nbsp; &nbsp; &nbsp; &nbsp; })&nbsp; &nbsp; }()&nbsp; &nbsp; go func(){&nbsp; &nbsp; &nbsp; &nbsp; Server().AppendRequest(func() {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; t := time.Now()&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fmt.Println("Calling func3 (time: " + t.Format("15:04:05") + ")")&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // do some stuff&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bCh <- true&nbsp; &nbsp; &nbsp; &nbsp; })&nbsp; &nbsp; }()&nbsp; &nbsp; // Wait for all 3 calls to come back&nbsp; &nbsp; for count := 0; count < 3; count++ {&nbsp; &nbsp; &nbsp; &nbsp; select {&nbsp; &nbsp; &nbsp; &nbsp; case val1 = <-iCh:&nbsp; &nbsp; &nbsp; &nbsp; case val2 = <-sCh:&nbsp; &nbsp; &nbsp; &nbsp; case val3 = <-bCh:&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }&nbsp; &nbsp; return}// SERVER LOGIC// Factory function that will only create a single serverfunc Server() *server {&nbsp; &nbsp; // Only one server for the entire application&nbsp; &nbsp; createServerOnce.Do(func() {&nbsp; &nbsp; &nbsp; &nbsp; s = &server{ticker: time.NewTicker(requestFreq), requests: []request{}}&nbsp; &nbsp; &nbsp; &nbsp; // Start a thread to make requests.&nbsp; &nbsp; &nbsp; &nbsp; go s.makeRequests()&nbsp; &nbsp; })&nbsp; &nbsp; return s}func (s *server) makeRequests() {&nbsp; &nbsp; if s == nil || s.ticker == nil {&nbsp; &nbsp; &nbsp; &nbsp; return&nbsp; &nbsp; }&nbsp; &nbsp; // This will keep going once per each requestFreq&nbsp; &nbsp; for _ = range s.ticker.C {&nbsp; &nbsp; &nbsp; &nbsp; var r request&nbsp; &nbsp; &nbsp; &nbsp; // You can't just access s.requests because you are in a goroutine&nbsp; &nbsp; &nbsp; &nbsp; // here while someone could be adding new requests outside of the&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; // goroutine so you have to use locks.&nbsp; &nbsp; &nbsp; &nbsp; s.Lock()&nbsp; &nbsp; &nbsp; &nbsp; if len(s.requests) > 0 {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // We have a lock here, which blocks all other operations&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // so just shift the first request out, save it and give&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // the lock back before doing any work.&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; r = s.requests[0]&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; s.requests = s.requests[1:]&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; s.Unlock()&nbsp; &nbsp; &nbsp; &nbsp; if r != nil {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // make the request!&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; r()&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }}func (s *server) AppendRequest(r request) {&nbsp; &nbsp; if s == nil {&nbsp; &nbsp; &nbsp; &nbsp; return&nbsp; &nbsp; }&nbsp; &nbsp; s.Lock()&nbsp; &nbsp; s.requests = append(s.requests, r)&nbsp; &nbsp; s.Unlock()}

慕神8447489

我原以为使用某种信号量或工作池更容易。这样,您可以做任何事情的工人数量有限。也可以有多个工作池。您是否需要这些调用中的任何一个是并发/异步的?如果没有,它们可以被调用,以便你可以有可配置的睡眠(一个讨厌的黑客头脑)。尝试工作池或信号量而不是函数的 chan。
随时随地看视频慕课网APP

相关分类

Go
我要回答