31

Go语言一知半解上手记(三)

 4 years ago
source link: https://studygolang.com/articles/26645
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

  在前两篇文章《 Go语言一知半解上手记(一) 》、《 Go语言一知半解上手记(二) 》中,我们实现了从读取 xlsx 格式的数据字典开始,到生成 schema 文件,到最后生成代码文件的完整过程。

  在 文章二 中,我们在 template.go 中添加了解析 schema 的方法 analyzeSchema ,在这个方法里,又调用了 generateModelFile、generateControllerFile 来生成最后的代码文件。这两个方法对数据字典格式的针对性及强,参考性不大,因此文章中未进行描述。在后面的实现过程中,我们又增加了 router 中使用的代码脚本的生成功能,最终,在 analyzeSchema 中便有了三个代码文件生成函数的调用,他们目前是顺序执行的,而Go语言提供了很方便的并发机制,我们可以再将这三个函数改造成并发执行的过程中初步了解一下Go语言的并发机制。

二、Go语言并发机制初探

1、并发前的 analyzeSchema 函数

// 分析模式文件
func analyzeSchema(schemaPath string, modelTmplPath string, controllerTmplPath string, consoleRouterTmplPath string) error {
	// 1.加载 模式文件
	schema, err := ioutil.ReadFile(schemaPath)
	if err != nil {
		return err
	}
	// 2.转换 模式文件到 数据字典集合 中
	var dataDictSlice []DataDict // 数据字典集合
	if err := Json.Unmarshal(schema, &dataDictSlice); err != nil {
		return err
	}

	// 3.加载模板文件
	modelTmpl, err := template.ParseFiles(modelTmplPath)
	if err != nil {
		return err
	}
	controllerTmpl, err := template.ParseFiles(controllerTmplPath)
	if err != nil {
		return err
	}
	consoleRouterTmpl, err := template.ParseFiles(consoleRouterTmplPath)
	if err != nil {
		return err
	}

	// 4.遍历数据字典集合
	wg := sync.WaitGroup{}
	for _, dataDict := range dataDictSlice {

		// 4.1.生成 collectionInfo 信息
		collectionInfo := map[string]string{
			"Model":          strings.ToUpper(string(dataDict.Collection.Name[0])) + dataDict.Collection.Name[1:],
			"CollectionName": dataDict.Collection.Name,
			"CollectionDesc": dataDict.Collection.Desc,
		}

		// 4.2.生成 所需 脚本
		modelScript, joiSchemaScript, err := getFieldsScript(dataDict)
		if err != nil {
			return nil
		}

		// 4.3.生成 model 代码文件
		err = generateModelFile(modelTmpl, collectionInfo, modelScript)
		if err != nil {
			println(err.Error())
		}

		// 4.4.生成 consoleRouter 代码片段
		err = generateCodeFile(consoleRouterTmpl, "./dist/consoleRouters/"+collectionInfo["CollectionName"]+".js", collectionInfo)
		if err != nil {
			println(err.Error())
		}

		// 4.5.生成 controller 代码文件
		collectionInfo["joiSchemaScript"] = joiSchemaScript
		err = generateCodeFile(controllerTmpl, "./dist/controllers/"+dataDictInfo["collectionInfo"]+".js", collectionInfo)
		if err != nil {
			println(err.Error())
		}

	}

	// 5.没有出错,返回 nil
	return nil
}
复制代码

与上一篇文章中的 analyzeSchema 函数不同之处如下:

  1. “加载模板文件” 时 由一个变成了三个
  2. “遍历数据字典集合” 时 将代码生成函数中所需要用到的信息 提前获取出来,分别是 collectionInfo、modelScript、joiSchemaScript
  3. 生成 controller 文件 及 consoleRouter 文件 的函数合并成了一个函数

2、并发后的 analyzeSchema 函数

// 分析模式文件
func analyzeSchema(schemaPath string, modelTmplPath string, controllerTmplPath string, consoleRouterTmplPath string) error {
	// 1.加载 模式文件
	schema, err := ioutil.ReadFile(schemaPath)
	if err != nil {
		return err
	}
	// 2.转换 模式文件到 数据字典集合 中
	var dataDictSlice []DataDict // 数据字典集合
	if err := Json.Unmarshal(schema, &dataDictSlice); err != nil {
		return err
	}

	// 3.加载模板文件
	modelTmpl, err := template.ParseFiles(modelTmplPath)
	if err != nil {
		return err
	}
	controllerTmpl, err := template.ParseFiles(controllerTmplPath)
	if err != nil {
		return err
	}
	consoleRouterTmpl, err := template.ParseFiles(consoleRouterTmplPath)
	if err != nil {
		return err
	}

	// 4.遍历数据字典集合,采用 协程 并发处理
	wg := sync.WaitGroup{}
	for _, dataDict := range dataDictSlice {

		// 4.1.生成 collectionInfo 信息
		collectionInfo := map[string]string{
			"Model":          strings.ToUpper(string(dataDict.Collection.Name[0])) + dataDict.Collection.Name[1:],
			"CollectionName": dataDict.Collection.Name,
			"CollectionDesc": dataDict.Collection.Desc,
		}

		// 4.2.生成 fields 脚本
		modelScript, joiSchemaScript, err := getFieldsScript(dataDict)
		if err != nil {
			return nil
		}

		// 4.3.生成 model 代码文件
		wg.Add(1)

		go func() {
			defer wg.Done()
			err := generateModelFile(modelTmpl, collectionInfo, modelScript)
			if err != nil {
				println(err.Error())
			}
		}()

		// 4.4.生成 consoleRouter 代码片段
		wg.Add(1)
		go func() {
			defer wg.Done()
			err := generateCodeFile(consoleRouterTmpl, "./dist/consoleRouters/"+collectionInfo["CollectionName"]+".js", collectionInfo)
			if err != nil {
				println(err.Error())
			}
		}()

		// 4.5.生成 controller 代码文件
		collectionInfo["joiSchemaScript"] = joiSchemaScript
		wg.Add(1)
		go func() {
			defer wg.Done()
			err := generateCodeFile(controllerTmpl, "./dist/controllers/"+collectionInfo["CollectionName"]+".js", collectionInfo)
			if err != nil {
				println(err.Error())
			}
		}()

	}
	wg.Wait()

	// 5.没有出错,返回 nil
	return nil
}
复制代码

具体改造如下:

  1. 使用 go func() { } () 将原来的执行方法包起来,实现 goroutine 并发任务
  2. 使用 sync.WaitGroup 来阻塞等待所有任务的完成,wg.Add(1)、wg.Done()、wg.Wait()

三、总结

  通过上面的小改造,我们把顺序执行的三个函数更改为了并发执行的任务,再通过 sync.WaitGroup 阻塞等待所有任务的完成。我们通过两种不同实现方法上的程序运行时间,也验证了并发确实是节省了时间的。这个例子中对于并发的应用是比较简单的,更深入的使用,就待日后再实战中继续探索吧。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK