Go语言并发范式之future模式详解

1、Go语言并发范式-future模式 编程中经常遇到在一个流程中需要调用多个子调用的情况,这些子调用相互之间没有依赖,如果串行地调用,则耗时会很长,此时可以使

1、Go语言并发范式-future模式

编程中经常遇到在一个流程中需要调用多个子调用的情况,这些子调用相互之间没有依赖,如果串行地调用,则耗时会很长,此时可以使用Go并发编程中的future模式。

future模式的基本工作原理:

(1)、使用chan作为函数参数。

(2)、启动goroutine调用函数。

(3)、通过chan传入参数。

(4)、做其他可以并行处理的事情。

(5)、通过chan异步获取结果。

package main
import (
	"fmt"
	"time"
)
// 一个查询结构体
// 这里的sql和result是一个简单的抽象,具体的应用,可能是更复杂的数据类型
type query struct {
	//参数Channel
	sql chan string
	//结果Channel
	result chan string
}
//执行Query
func execQuery(q query) {
	//启动协程
	go func() {
		//获取输入
		sql := <-q.sql
		//访问数据库
		//输出结果通道
		q.result <- "result from " + sql
	}()
}
func main() {
	//初始化Query
	q := query{make(chan string, 1), make(chan string, 1)}
	//执行Query,注意执行的时候无需准备参数
	go execQuery(q)
	//准备参数
	q.sql <- "select * from table;"
	//do otherthings
	time.Sleep(1 * time.Second)
	//获取结果
	fmt.Println(<-q.result)
}

程序输出

result from select * from table;

future最大的好处是将函数的同步调用转换为异步调用,适用于一个交易需要多个子调用且这些子调用没有依赖

的场景。实际情况可能比上面示例复杂得多,要考虑错误和异常的处理,读者着重体验这种思想,而不是细节。

2、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 <- result
	defer close(futureTask.res)
	return
}

(3)、测试代码

package main
import (
	"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 <- result
	defer 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 main
import (
	"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 <- result
	defer 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)
}

到此这篇关于Go语言并发范式之future模式详解的文章就介绍到这了,更多相关Go future模式内容请搜索好代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持好代码网!

您可能有感兴趣的文章
golang并发编程的如何实现

golang如何实现redis的延时消息队列功能示例

golang中range在slice和map遍历中的注意事项

Golang命令行进行debug调试操作

golang的空标识符理解