Futures/Promises Golang方式实现

原文:http://labs.strava.com/blog/futures-in-golang/

我并没有打算逐字逐句的翻译,我觉得太死板, 所以我把核心大意说一下就好,如果您希望逐字逐句斟酌, 请看原文链接!

通常网络程序中, 大量时间花费在请求和响应上, 如果需要发送的请求比较多, 若以同步方式,则必然是发送请求,等待响应;

然后再发下一个请求并等待结果返回, 这样依次处理,其效率可想而知, 比较低下!若以异步方式,则可将所有请求并行发出,

而每一个请求的响应则以回调方式异步处理,这样效率自然是非常高,但是异步回调方式不直观,并碎片化业务代码!所以比较好

的方案就是: 代码形式是同步的,执行实质是异步的,当属:Futures/Promises, 其它语言早已有之。 


见go代码:


package main

import (
    "io/ioutil"
    "log"
    "net/http"
)

func RequestFuture(url string) <-chan []byte {
    c := make(chan []byte, 1)
    go func() {
        var body []byte
        defer func() { 
            c <- body
        }()

        resp, err := http.Get(url)
        if err != nil {
            return
        }
        defer resp.Body.Close()

        body, _ = ioutil.ReadAll(resp.Body) //同步阻塞调用,直到响应返回。
    }()

    return c
}

func main() {
    future := RequestFuture("http://labs.strava.com")

    // do many other things, maybe create other futures

    body := <-future
    log.Printf("response length: %d", len(body))
}
以上代码少而直观,不需多言,其缺点为:出错时channel只会返加nil/empty, 调用者拿不掉错误信息, 所以需改进。

见go代码:

unc RequestFutureFunction(url string) func() ([]byte, error) {
    var body  []byte
    var err error

    c := make(chan struct{}, 1)
    go func() {
        defer close(c) //1. 响应拿到了,并闭c.

        var resp *http.Response
        resp, err = http.Get(url)
        if err != nil {
            return
        }
        defer resp.Body.Close()
        body, err = ioutil.ReadAll(resp.Body)//2.同步阻塞调用,直到响应返回。
    }()

    return func() ([]byte, error) {
        <-c //3.因为1处关闭了c,则此处不再同步阻塞等待,即响应已拿到,所以向下执行.
        return body, err
    }
}
func main() {
    future := RequestFutureFunction("http://strava.com")
    
    // do many other things, maybe create other futures

    body, err := future()

    log.Printf("response length: %d", len(body))
    log.Printf("request error: %v", err)
}
以上代码主要改进:调用者可以拿到错误信息,再者以上代码返回的不再是channel而是一个function, 可以多次调用,即多次读取响应

而第一个版本,只能从channel中读取一次响应信息。以上代码可以工作得不错, 但是用起来麻烦且不通用, 所以要抽象一下。

见go代码:


func Future(f func() (interface{}, error)) func() (interface{}, error) {
    var result interface{}
    var err error

    c := make(chan struct{}, 1)
    go func() {
        defer close(c)
        result, err = f()
    }()

    return func() (interface{}, error) {
        <-c
        return result, err
    }
}
func main() {
    url := "http://labs.strava.com"
    future := Future(func() (interface{}, error) {
        resp, err := http.Get(url)
        if err != nil {
            return nil, err
        }
        defer resp.Body.Close()
        return ioutil.ReadAll(resp.Body)
    })

    // do many other things

    b, err := future()
    body, _ := b.([]byte)

    log.Printf("response length: %d", len(body))
    log.Printf("request error: %v", err)
}
以上代码为抽象封装后的代码, 使用时, 需调用者传入同步执行的函数,而以上代码对这个同步函数进行异步封装,成为Futures/Promises.

其实代码改进到此处,已经非常明晰好用,只是因为golang不支持C++那样的模板机制, 所以存在:[]byte -> interface{} -> []byte.

这样的数据类型转换,所以在使用时, 如果数据类型不匹配, 则类型转换很可能失败引发运行时异常, 所以此篇

文章作者给出一个方案:代码生成器,实现代码我没看, 估计是不同的类型实现,用id表记,再提供一个工厂方法,

这样就可以以id获取对应数据类型的Futures/Promises代码实现,具体如何,您有时间可以深入研究一下!


注意: 此文章只是我个人笔记, 如有错漏,请一定指正, 共同学习, 我的邮箱: htyu_0203_39@sina.com


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
[/public/home/pengjy/anaconda3] >>> PREFIX=/public/home/pengjy/anaconda3 WARNING: md5sum mismatch of tar archive expected: 8a581514493c9e0a1cbd425bc1c7dd90 got: 614f6284c34f91affd38a1be2e4be076 - Unpacking payload ... Traceback (most recent call last): File "entry_point.py", line 76, in <module> File "tarfile.py", line 2024, in extractall File "tarfile.py", line 2065, in extract File "tarfile.py", line 2137, in _extract_member File "tarfile.py", line 2186, in makefile File "tarfile.py", line 249, in copyfileobj tarfile.ReadError: unexpected end of data [210095] Failed to execute script entry_point concurrent.futures.process._RemoteTraceback: ''' Traceback (most recent call last): File "concurrent/futures/process.py", line 368, in _queue_management_worker File "multiprocessing/connection.py", line 251, in recv TypeError: __init__() missing 1 required positional argument: 'msg' ''' The above exception was the direct cause of the following exception: Traceback (most recent call last): File "entry_point.py", line 69, in <module> File "concurrent/futures/process.py", line 484, in _chain_from_iterable_of_lists File "concurrent/futures/_base.py", line 611, in result_iterator File "concurrent/futures/_base.py", line 439, in result File "concurrent/futures/_base.py", line 388, in __get_result concurrent.futures.process.BrokenProcessPool: A process in the process pool was terminated abruptly while the future was running or pending. [210105] Failed to execute script entry_point 是什么问题,如何解决?
最新发布
07-25

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值