看到公司其他同学写的 go 批量处理代码,这风骚的感觉像在平行世界一样,还能这么玩?

63 天前
 PungentSauce

这里是一些脚本调用的地方,工具源码放在后面两个代码块了。

util.TaskConsumer[[]string](10).
		SetP(lineopt.IterExcel2("xxx.xlsx")).
		SetC(func(index int, row []string) (err error) {
			if index == 1 {
				return
			}
			// .....
			// 这里是逻辑处理函数
			return
		}).
		Run()

这是两个封装的函数的源码。

package lineopt

import (
	"bufio"
	"fmt"
	"github.com/xuri/excelize/v2"
	"iter"
	"log/slog"
	"os"
)

func IterLine2(filePath string) iter.Seq2[int, string] {
	return func(yield func(int, string) bool) {
		f, errF := os.OpenFile(filePath, os.O_RDONLY, 0666)
		if errF != nil {
			return
		}
		defer func(f *os.File) {
			err := f.Close()
			if err != nil {
				fmt.Println(err)
			}
		}(f)
		scanner := bufio.NewScanner(f)
		index := 1
		for scanner.Scan() {
			line := scanner.Text()
			if !yield(index, line) {
				return
			}
			index += 1
		}
	}
}

func IterLine(filePath string) iter.Seq[string] {
	return func(yield func(string) bool) {
		for _, item := range IterLine2(filePath) {
			if !yield(item) {
				return
			}
		}
	}
}

func MapIterExcel2(config ExcelTarget) iter.Seq2[int, []string] {
	return func(yield func(int, []string) bool) {
		f, err := excelize.OpenFile(config.FilePath)
		if err != nil {
			slog.Error(err.Error())
			return
		}
		defer f.Close()
		targetSheet := config.TargetSheet
		if targetSheet == "" {
			targetSheet = f.GetSheetName(0)
		}
		rows, err := f.Rows(targetSheet)
		if err != nil {
			slog.Error(err.Error())
			return
		}
		index := 1
		for rows.Next() {
			row, err := rows.Columns()
			if err != nil {
				slog.Error(err.Error())
				return
			}
			if !yield(index, row) {
				return
			}
			index += 1
		}
		return
	}
}

func MapIterExcel(config ExcelTarget) iter.Seq[[]string] {
	return func(yield func([]string) bool) {
		for _, value := range MapIterExcel2(config) {
			if !yield(value) {
				return
			}
		}
	}
}

func IterExcel2(filePath string) iter.Seq2[int, []string] {
	return func(yield func(int, []string) bool) {
		for index, value := range MapIterExcel2(ExcelTarget{FilePath: filePath}) {
			if !yield(index, value) {
				return
			}
		}
	}
}

func IterExcel(filePath string) iter.Seq[[]string] {
	return func(yield func([]string) bool) {
		for _, value := range MapIterExcel2(ExcelTarget{FilePath: filePath}) {
			if !yield(value) {
				return
			}
		}
	}
}

func IterExcelSheet2(filePath string, sheetName string) iter.Seq2[int, []string] {
	return func(yield func(int, []string) bool) {
		for index, value := range MapIterExcel2(ExcelTarget{
			FilePath:    filePath,
			TargetSheet: sheetName,
		}) {
			if !yield(index, value) {
				return
			}
		}
	}
}

func IterExcelSheet(filePath string, sheetName string) iter.Seq[[]string] {
	return func(yield func([]string) bool) {
		for _, value := range MapIterExcel2(ExcelTarget{
			FilePath:    filePath,
			TargetSheet: sheetName,
		}) {
			if !yield(value) {
				return
			}
		}
	}
}
package util

import (
	"dt/app/util/lineopt"
	"errors"
	"iter"
	"sync"
)

func ChannelConsume[d any](queue chan d, job func(item d), number ...int) *sync.WaitGroup {
	counter := 10
	if len(number) == 1 && number[0] > 0 {
		counter = number[0]
	}
	return StartTogether(func() {
		for item := range queue {
			job(item)
		}
	}, counter)
}

// Together 并行执行
func Together(job func(), counter int) {
	var wg sync.WaitGroup
	for i := 1; i <= counter; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			job()
		}()
	}
	wg.Wait()
}

func StartTogether(job func(), counter int) *sync.WaitGroup {
	var wg sync.WaitGroup
	for i := 1; i <= counter; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			job()
		}()
	}
	return &wg
}

type chanData[d any] struct {
	index int
	data  d
}

func ChannelConsume2[d any](queue chan chanData[d], job func(index int, item d) (err error), number ...int) *sync.WaitGroup {
	counter := 10
	if len(number) == 1 && number[0] > 0 {
		counter = number[0]
	}
	return StartTogether(func() {
		for item := range queue {
			err := job(item.index, item.data)
			if errors.Is(err, lineopt.Stop) {
				// 目前不可以直接停止,会导致消费者阻塞掉
				//return
			}
		}
	}, counter)
}

type ProducerConsumer[T any] struct {
	consumerNumber int
	queue          chan chanData[T]
	p              iter.Seq2[int, T]
	c              func(index int, item T) (err error)
	once           sync.Once
}

func (itself *ProducerConsumer[T]) SetC(c func(index int, item T) (err error)) *ProducerConsumer[T] {
	itself.c = c
	return itself
}

func (itself *ProducerConsumer[T]) SetP(p iter.Seq2[int, T]) *ProducerConsumer[T] {
	itself.p = p
	return itself
}

// 生产者消费者都有可能发生阻塞,
// 生产者阻塞的原因是因为 queue 容量不够了
// 消费者阻塞的原因的是因为 queue 没有 close
// 生产者只需要实现即可
func (itself *ProducerConsumer[T]) do() {
	task := ChannelConsume2(itself.queue, func(index int, item T) (err error) {
		return itself.c(index, item)
	}, itself.consumerNumber)
	defer task.Wait()
	defer close(itself.queue)
	for index, v := range itself.p {
		select {
		case itself.queue <- chanData[T]{
			index,
			v,
		}:
			break
			// 需要一个可以知道提前截止的操作
		}
	}

}

func (itself *ProducerConsumer[T]) Run() {
	itself.once.Do(func() {
		itself.do()
	})
}

func TaskConsumer[T any](consumerNumber ...int) *ProducerConsumer[T] {
	n := 1
	if len(consumerNumber) > 0 {
		n = consumerNumber[0]
	}
	return &ProducerConsumer[T]{
		queue:          make(chan chanData[T], n),
		consumerNumber: n,
	}
}

6105 次点击
所在节点    Go 编程语言
28 条回复
PungentSauce
63 天前
感觉调用的地方有点不像 go 了
aladdinding
63 天前
yield 乍一看以为是 py
henix
63 天前
用了 Go 最新的 iterator 搞出了一些偏函数式风格的东西。似乎是为了方便并发处理

P.S. 如果这是公司的代码,一般公司都不会允许随便发布到公开的地方
RedisMasterNode
63 天前
P.S. 如果这是公司的代码,一般公司都不会允许随便发布到公开的地方

或许没人管,或许也不是多好的代码,但是要有职业素养...
vincentWdp
63 天前
一股 Java 味儿
PungentSauce
63 天前
@RedisMasterNode @henix 同学的本地工具代码,不是生产环境用的。
PungentSauce
63 天前
@PungentSauce 不是项目使用的。公司代码都是 java
PungentSauce
63 天前
感觉比较 6 ,但是风格和其他看到的不大一样。
Nanosk
63 天前
这写法看到就烦 谁爱维护谁维护。。
fumeboy
63 天前
大哥。函数式编程都没见过?
jheroy
63 天前
这种代码就是写的人感觉很爽,看的人想骂娘。
v1
63 天前
链式编程……这不是基本功吗……反过来说,java 那套继承就很……
archxm
63 天前
前端风格吗?
mightybruce
63 天前
这代码没什么问题, 不知道你想说什么。
lysShub
63 天前
垃圾代理,而且函数变量很影响性能
strobber16
63 天前
这就是 go ,不爽不要来
nkidgm
63 天前
go 我都是用来做命令行小程序,业务层面的代码可不敢这样子写。
kneo
63 天前
有啥问题?
iceheart
62 天前
过两年作者自己也看不懂了。
Silicon
62 天前
这种风骚属于脱离生态后不得已而为之,带有强迫的属性,所以是其他「受害者」。
上游先有一个预处理器把 excel 干掉,剩下的事情就好办很多……

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://ex.noerr.eu.org/t/1147599

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX