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最大的好处是将函数的同步调用转换为异步调用,适用于一个交易需要多个子调用且这些子调用没有依赖

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

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)

}

你可能感兴趣的:(golang,golang)