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

 更新时间:2023年06月15日 10:46:06   作者:242030  
编程中经常遇到在一个流程中需要调用多个子调用的情况,此时就可以使用Go并发编程中的future模式,下面小编就来和大家聊聊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 filepath包常用函数详解

    Golang filepath包常用函数详解

    本文介绍与文件路径相关包,该工具包位于path/filepath中,该包试图与目标操作系统定义的文件路径兼容。本文介绍一些常用函数,如获取文件绝对路径,获取文件名或目录名、遍历文件、分割文件路径、文件名模式匹配等函数,并给具体示例进行说明
    2023-02-02
  • GO语言的数组array与切片slice详解

    GO语言的数组array与切片slice详解

    这篇文章主要介绍了GO语言的数组array与切片slice,包括了GO语言数组定义赋值,GO语言多维数组,GO语言切片等知识点需要的朋友可以参考下
    2022-12-12
  • 聊聊Go语言编译github上的项目遇到的坑

    聊聊Go语言编译github上的项目遇到的坑

    这篇文章主要介绍了解决Go语言编译github上的项目遇到的坑,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-04-04
  • 完美解决beego 根目录不能访问静态文件的问题

    完美解决beego 根目录不能访问静态文件的问题

    下面小编就为大家带来一篇完美解决beego 根目录不能访问静态文件的问题。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-06-06
  • GO语言中defer实现原理的示例详解

    GO语言中defer实现原理的示例详解

    这篇文章主要为大家详细介绍了Go语言中defer实现原理的相关资料,文中的示例代码讲解详细,对我们学习Go语言有一定的帮助,需要的可以参考一下
    2023-02-02
  • GO 集合 map 使用示例小结

    GO 集合 map 使用示例小结

    Go语言的集合称为映射(map),它是一种无序的键值对(key-value)的集合,集合是通过键(key)来快速检索值(value)的,键(key)类似于索引,它指向值(value)的数据,这篇文章主要介绍了GO集合map使用总结,本文通过示例代码给大家介绍的非常详细,需要的朋友可以参考下
    2023-06-06
  • go语言题解LeetCode674最长连续递增序列

    go语言题解LeetCode674最长连续递增序列

    这篇文章主要为大家介绍了go语言题解LeetCode674最长连续递增序列示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-12-12
  • Go语言定时任务的实现示例

    Go语言定时任务的实现示例

    本文主要介绍了Go语言定时任务的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-05-05
  • 使用golang在windows上设置全局快捷键的操作

    使用golang在windows上设置全局快捷键的操作

    最近在工作中,总是重复的做事,想着自己设置一个快捷键实现windows 剪贴板的功能,所以本文小编给大家分享了使用golang在windows上设置全局快捷键的操作,文中有相关的代码示例供大家参考,需要的朋友可以参考下
    2024-02-02
  • Go语言题解LeetCode463岛屿的周长示例详解

    Go语言题解LeetCode463岛屿的周长示例详解

    这篇文章主要为大家介绍了Go语言题解LeetCode463岛屿的周长示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-12-12

最新评论