1、Go语言并发范式-future模式
编程中经常遇到在一个流程中需要调用多个子调用的情况,这些子调用相互之间没有依赖,如果串行地调用,则耗
时会很长,此时可以使用Go并发编程中的future
模式。
future
模式的基本工作原理:
(1)、使用chan
作为函数参数。
(2)、启动goroutine
调用函数。
(3)、通过chan
传入参数。
(4)、做其他可以并行处理的事情。
(5)、通过chan
异步获取结果。
package mainimport ("fmt""time"
)// 一个查询结构体
// 这里的sql和result是一个简单的抽象,具体的应用,可能是更复杂的数据类型
type query struct {//参数Channelsql chan string//结果Channelresult chan string
}//执行Query
func execQuery(q query) {//启动协程go func() {//获取输入sql := <-q.sql//访问数据库//输出结果通道q.result <- "result from " + sql}()
}func main() {//初始化Queryq := query{make(chan string, 1), make(chan string, 1)}//执行Query,注意执行的时候无需准备参数go execQuery(q)//准备参数q.sql <- "select * from table;"//do otherthingstime.Sleep(1 * time.Second)//获取结果fmt.Println(<-q.result)
}
# 程序输出
result from select * from table;
future
最大的好处是将函数的同步调用转换为异步调用,适用于一个交易需要多个子调用且这些子调用没有依赖
的场景。实际情况可能比上面示例复杂得多,要考虑错误和异常的处理,读者着重体验这种思想,而不是细节。
Future模式的实现步骤:
(1)、构建结构体FutureTask
这里我们将要做的事情抽象成任务
,对于每个任务我们可能需要传递参数
过去,并且我们还需要得到这个任务的
执行结果
,为此,我们创建两个channel
,一个用于传递参数,一个用于保存结果。(具体还需要什么其他的参
数可以根据具体业务进行设计)。
// FutureTask 在并发执行时用于传递参数和保存返回的结果
type FutureTask struct {// 用于传递参数args chan interface{}// 实际业务中可能还有很多其他的数据// 用于保存结果res chan interface{}
}
(2)、创建goroutine执行future的方法
在创建好FutureTask
之后,需要开启goroutine
去执行,为此需要创建一个执行FutureTask
的方法:
// execFutureTask 用于开启一个Future模式的线程
func execFutureTask(futureTask *FutureTask) {// 读取传入的参数fmt.Println("goroutine读取到的参数:", <-futureTask.args)// 这里可以执行具体的业务逻辑result := "执行完业务逻辑后得到的结果"// 将结果进行保存futureTask.res <- resultdefer close(futureTask.res)return
}
(3)、测试代码
package mainimport ("fmt""time"
)// FutureTask 在并发执行时用于传递参数和保存返回的结果
type FutureTask struct {// 用于传递参数args chan interface{}// 实际业务中可能还有很多其他的数据// 用于保存结果res chan interface{}
}// execFutureTask 用于开启一个Future模式的线程
func execFutureTask(futureTask *FutureTask) {// 读取传入的参数fmt.Println("goroutine读取到的参数:", <-futureTask.args)// 这里可以执行具体的业务逻辑result := "执行完业务逻辑后得到的结果"// 将结果进行保存futureTask.res <- resultdefer close(futureTask.res)return
}func main() {// 创建一个FutureTask并开启一个goroutine去执行futureTask := FutureTask{make(chan interface{}), make(chan interface{})}go execFutureTask(&futureTask)// 向FutureTask传入参数,如果不传的话会死锁futureTask.args <- "main线程传入的参数"// 这里可以并行的去执行一些其他业务逻辑time.Sleep(1 * time.Second)// 读取线程执行的fmt.Println("主线程读取future模式下goroutine的结果:", <-futureTask.res)}
(4)、执行结果
// 程序输出
goroutine读取到的参数: main线程传入的参数
主线程读取future模式下goroutine的结果: 执行完业务逻辑后得到的结果
(5)、完整代码
package mainimport ("fmt""time"
)// FutureTask 在并发执行时用于传递参数和保存返回的结果
type FutureTask struct {// 用于传递参数args chan interface{}// 实际业务中可能还有很多其他的数据// 用于保存结果res chan interface{}
}// execFutureTask 用于开启一个Future模式的线程
func execFutureTask(futureTask *FutureTask) {// 读取传入的参数fmt.Println("goroutine读取到的参数:", <-futureTask.args)// 这里可以执行具体的业务逻辑result := "执行完业务逻辑后得到的结果"// 将结果进行保存futureTask.res <- resultdefer close(futureTask.res)return
}func main() {// 创建一个FutureTask并开启一个goroutine去执行futureTask := FutureTask{make(chan interface{}), make(chan interface{})}go execFutureTask(&futureTask)// 向FutureTask传入参数,如果不传的话会死锁futureTask.args <- "main线程传入的参数"// 这里可以并行的去执行一些其他业务逻辑time.Sleep(1 * time.Second)// 读取线程执行的fmt.Println("主线程读取future模式下goroutine的结果:", <-futureTask.res)}