musicfox 2.0终于完成了,没有太多的功能变更,主要使用golang进行重写,优化了用户体验(主要是Windows下)。
存在的问题与方案
musicfox最开始是用Dart写的,工作原理大致是主进程fork一个mpg123的子进程,主进程处理UI渲染、用户交互及网络请求等,然后通过进程间管道通信,来控制mpg123子进程的音乐播放、暂停以及播放器的音量大小等。
这种模式下,会存在以下这些问题:
- 依赖mpg123,需要用户手动安装mpg123
- mpg123只能播放mp3音乐,网易云无损音质是flac格式,无法播放
- 进程间需要通过管道进行通信,Windows下不容易处理
- Dart大多数三方包都是基于Flutter的,原生包不多
- ...
为了解决这些问题,于是开始采用golang进行重写,优势主要是:
- golang支持更多平台的二进制编译
- 拥有丰富的第三方包,例如音频播放的beep、TUI渲染的
bubbletea、本地数据库的bolt等等 - 不需要借助其他进程,所以不存在进程通信
- 支持mp3、flac、wav等格式音频文件的解码播放
- goroutine + chan
另外对我自己来说可以深入熟悉golang...
使用go时遇到的问题
协程泄漏
在使用golang过程中遇到比较严重的问题就是协程泄漏。例如下面的代码:
package main
import (
"fmt"
"runtime"
)
func main() {
c1 := make(chan struct{})
for i := 0; i < 10; i++ {
go test(c1)
fmt.Println(runtime.NumGoroutine())
}
}
func test(c chan<- struct{}) {
c <- struct{}{}
}
执行后会输出:
2
3
4
5
6
7
8
9
10
11
可见,协程数一直在增加,这是因为没有消费者消费chan中的数据,所有子协程都阻塞在写chan的操作。
类似这种情况就会导致协程数不断增长,出现协程泄漏,内存占用不断上升。
如何尽量避免协程泄漏
- 确保chan都会有相应的消费者、生产者,且生产速度和消费速度差不多
- 如果需要往chan中压数据或读数据,但又不确定是否有消费者或生产者,可以使用
select
结构避免阻塞 - 使用goroutine + chan处理,避免创建大量协程,例如:
package main
import (
"fmt"
"runtime"
"time"
)
func main() {
var data [100]string
// 获取data逻辑
for _, v := range data {
go test(v)
}
fmt.Println(runtime.NumGoroutine())
}
func test(s string) {
// 假装业务逻辑
time.Sleep(time.Millisecond)
}
输出为: 101,也就是新开了100个协程,如果数据更多时,会产生更多协程。这种情况可以考虑创建固定数量的若干个协程,然后通过chan将数据投递给子协程进行处理,避免大量协程占用内存,如:
package main
import (
"fmt"
"runtime"
"time"
)
func main() {
var data [100]string
c := make(chan string)
for i := 0; i < 5; i++ {
go test(c)
}
for _, v := range data {
c <- v
}
fmt.Println(runtime.NumGoroutine())
}
func test(c <-chan string) {
for _ = range c {
// 业务逻辑
time.Sleep(time.Millisecond)
}
}
协程泄漏排查
- runtime.NumGoroutine()查看协程数
- 使用gops排查,可以查看当前协程数、调用栈等
预览
安装
Mac
提供两种方式安装:
使用brew安装
brew tap anhoder/go-musicfox && brew install go-musicfox
如果你之前安装过musicfox,需要使用下列命令重新链接:
brew unlink musicfox && brew link --overwrite go-musicfox
直接下载
下载Mac可执行文件,在iTerm或Terminal中打开
Linux
身边没有Linux设备,而播放音乐依赖CGO,无法进行交叉编译,所以暂时没有可用的二进制文件
Windows
下载Windows可执行文件,在命令行中运行即可。
推荐使用Windows Terminal,UI及体验会好很多
快捷键
按键 | 作用 | 备注 |
---|---|---|
h/H/LEFT | 左 | |
l/L/RIGHT | 右 | |
k/K/UP | 上 | |
j/J/DOWN | 下 | |
q/Q | 退出 | |
space | 暂停/播放 | |
[ | 上一曲 | |
] | 下一曲 | |
- | 减小音量 | |
= | 加大音量 | |
n/N/ENTER | 进入选中的菜单 | |
b/B/ESC | 返回上级菜单 | |
w/W | 退出并退出登录 | |
p | 切换播放方式 | |
P | 心动模式(仅在歌单中时有效) | |
r/R | 重新渲染UI | Windows调整窗口大小后,没有事件触发,可以使用该方法手动重新渲染 |
, | 喜欢当前播放歌曲 | |
< | 喜欢当前选中歌曲 | |
. | 当前播放歌曲移除出喜欢 | |
> | 当前选中歌曲移除出喜欢 | |
/ | 标记当前播放歌曲为不喜欢 | |
? | 标记当前选中歌曲为不喜欢 |