Skip to content

Commit

Permalink
feat(concurrency): add channel
Browse files Browse the repository at this point in the history
  • Loading branch information
shgopher committed Jan 17, 2024
1 parent 47b56f6 commit 6481565
Showing 1 changed file with 104 additions and 0 deletions.
104 changes: 104 additions & 0 deletions 并发/channel/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,89 @@ func main(){
}
```
通常来说,这是为了超时而去设置的跳出机制
### cron 规则定时任务框架 robfig/cron

robfig/cron 是一个 Go 语言编写的 Cron 计划任务管理器。

这个项目的主要功能和特点包括:

- 实现了 cron 规范,可以基于 cron 表达式来调度任务
- 支持在代码中以声明式方式定义定时任务
- 支持在配置文件中定义定时任务
- 支持任务链,一个任务可以触发其他任务
- 支持任务锁,防止并发执行
- 支持失败重试
- 有默认的日志记录器,也可以自定义日志记录器
- 轻量级,没有外部依赖

robfig/cron 因为其简单、轻量和高效而被许多 Go 项目用来实现定时任务。它可以用于项目中不同粒度的调度需求,如每分钟执行、每小时执行等。

下面解释一下 cron 机制的含义:

cron 表达式调度是 cron 作业调度程序的核心机制。

cron 表达式是一个字符串,由 5 或 6 个用空格分隔的字段组成,每个字段表示一个时间单位。Cron 会根据表达式中设置的值,在特定的时间点触发相应的任务。

cron 表达式的字段与意义如下:

- 第 1 个字段:分钟 (0-59)
- 第 2 个字段:小时 (0-23)
- 第 3 个字段:一个月中的第几天 (1-31)
- 第 4 个字段:月份 (1-12)
- 第 5 个字段:星期几 (0-7,0 和 7 都表示星期日)
- 第 6 个字段:年份 (可选,留空就表示每年)

每个字段可以使用特殊字符表示多种时间设置:

- `*`:表示匹配该字段的任意值
- `,`:表示分隔多个值,如在分钟字段使用 “5,15,25” 就表示5分、15分、25分都触发
- `-`:表示一个范围,如在小时字段使用 “9-17” 表示 9 点到 17 点之间每个整点都触发
- `/`:表示一个间隔,如在分钟字段使用 “0/15” 表示每15分钟触发一次,从0分开始
- `?`:表示不指定的值,会匹配该字段的任意值。

一些例子:

- `0 0 12 * * ?`:每天 12 点触发
- `0 15 10 ? * *`:每天 10:15 触发
- `0 0/5 14 * * ?`:每天 14:00 到 14:55 每5分钟触发一次
- `0 0/5 14,18 * * ?`:每天 14:00 到 14:55 和 18:00 到 18:55 每5分钟触发一次

通过这种表达式,cron 可以非常灵活地设置触发时间表,从而实现各种调度需求。

下面给出这个项目的 demo:

```go
c := cron.New()

// 每小时的第30分钟执行
c.AddFunc("30 * * * *", func() { fmt.Println("每小时的第30分钟") })

// 在每天早上3点到6点,晚上8点到11点的范围内,每小时执行一次
c.AddFunc("30 3-6,20-23 * * *", func() { fmt.Println("在每天早上3点到6点,晚上8点到11点的范围内,每小时执行一次") })

// 每天早上东京时间4点30分执行一次
c.AddFunc("CRON_TZ=Asia/Tokyo 30 04 * * *", func() { fmt.Println("每天早上东京时间4点30分执行一次") })

// 从现在起每小时执行一次
c.AddFunc("@hourly", func() { fmt.Println("从现在起每小时执行一次") })

// 从现在起每隔1小时30分执行一次
c.AddFunc("@every 1h30m", func() { fmt.Println("从现在起每隔1小时30分执行一次") })

c.Start()

// 被添加的函数会在自己的goroutine中异步执行
...

// 也可以向已经启动的Cron添加新的函数
c.AddFunc("@daily", func() { fmt.Println("每天执行") })

// 检查cron任务下次和上次的执行时间
inspect(c.Entries())

// 停止调度器(已经启动的任务不会被停止)
c.Stop()
```

## 使用反射执行未知数量的 channel
当你不确定需要多少个 channel 去处理时,你只能选择在运行时去创建未知数量的 channel,这个时候就要使用反射了。
Expand Down Expand Up @@ -698,10 +781,27 @@ func main() {
```
## 任务编排
### fan-in
核心思想就是 “多个进,单个出”,这是数字电路的一个概念,场景是,多个 channel 发送数据给一个处理函数,此函数返回一个 channel 去承载众多信息,我们只需要监听最后输出的 channel 就可以达到监听众多信息的目的。

基本样貌如下:
```go
func fanin(chs ...<-chan any) <-chan any {
}
```
从这个基本构型也能看出来,我们要在函数中处理多个 channel,这跟上文 channel 传递信号的 or-do 模式类似,所以免不了我们要使用反射或者递归的方式去处理函数,首先我们使用递归的方式去实现这个功能

递归版 `fan-in`
```go

```
### fan-out

### map-reduce

### pipeline-流水线模式

### stream-流模式

### pipeline 流水线模式和 stream 流模式的对比
流水线模式 (Pipeline Pattern) 和流模式 (Stream Pattern) 都是将任务分解成多个阶段来处理,但两者还是有一些区别:

Expand Down Expand Up @@ -768,9 +868,13 @@ func age(){
这里插一句,main goroutine 只要退出,其它 goroutine 不管有没有执行完毕也会退出,所以如果这种代码在 main 函数中出现,那么是不会发生 goroutine 泄露问题的,因为 main 函数结束以后,其它 goroutine 自动结束
## channel 的实现原理
### 创建

### send

### recive

### close

## issues
### channel 是并发银弹吗?

Expand Down

0 comments on commit 6481565

Please # to comment.