Skip to content

Commit eca7760

Browse files
authored
refactor: 使用引用计数的互斥锁优化 Emby 和 Jellyfin 的并发控制 (#82)
在 VideosHandler 中引入 mutexWithRefCount 类型,确保同一 item ID 的处理任务在并发情况下不会重复执行。通过原子操作管理引用计数,避免内存泄漏,提升性能和稳定性。
1 parent a7a5b70 commit eca7760

2 files changed

Lines changed: 47 additions & 11 deletions

File tree

internal/handler/emby.go

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,17 @@ import (
2121
"strconv"
2222
"strings"
2323
"sync"
24+
"sync/atomic"
2425

2526
"github.com/gin-gonic/gin"
2627
)
2728

29+
// 带引用计数的互斥锁
30+
type mutexWithRefCount struct {
31+
mu sync.Mutex
32+
refCount int32 // 使用 atomic 操作
33+
}
34+
2835
// Emby服务器处理器
2936
type EmbyServerHandler struct {
3037
server *emby.EmbyServer // Emby 服务器
@@ -261,12 +268,26 @@ func (embyServerHandler *EmbyServerHandler) VideosHandler(ctx *gin.Context) {
261268

262269
// 并发控制:确保同一个 item ID 只有一个任务在运行
263270
// 将整个处理流程放在锁内,避免重复查询和重复获取重定向 URL
264-
var mu *sync.Mutex
271+
var muWrapper *mutexWithRefCount
265272
if itemID != "" {
266-
mutex, _ := embyServerHandler.playbackInfoMutex.LoadOrStore(itemID, &sync.Mutex{})
267-
mu = mutex.(*sync.Mutex)
268-
mu.Lock()
269-
defer mu.Unlock()
273+
// 加载或创建 mutex wrapper
274+
value, _ := embyServerHandler.playbackInfoMutex.LoadOrStore(itemID, &mutexWithRefCount{})
275+
muWrapper = value.(*mutexWithRefCount)
276+
277+
// 增加引用计数
278+
atomic.AddInt32(&muWrapper.refCount, 1)
279+
280+
// 锁定并处理
281+
muWrapper.mu.Lock()
282+
defer func() {
283+
muWrapper.mu.Unlock()
284+
// 减少引用计数
285+
refCount := atomic.AddInt32(&muWrapper.refCount, -1)
286+
// 如果没有其他 goroutine 在使用,删除这个 mutex 以避免内存泄漏
287+
if refCount == 0 {
288+
embyServerHandler.playbackInfoMutex.Delete(itemID)
289+
}
290+
}()
270291
logging.Debugf("开始处理 item %s 的 VideosHandler 请求", itemID)
271292
}
272293

internal/handler/jellyfin.go

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"strconv"
2222
"strings"
2323
"sync"
24+
"sync/atomic"
2425

2526
"github.com/gin-gonic/gin"
2627
)
@@ -94,7 +95,7 @@ func (jellyfinHandler *JellyfinHandler) GetImageCacheRegexp() *regexp.Regexp {
9495
return constants.JellyfinRegexp.Cache.Image
9596
}
9697

97-
func (JellyfinHandler) GetSubtitleCacheRegexp() *regexp.Regexp {
98+
func (*JellyfinHandler) GetSubtitleCacheRegexp() *regexp.Regexp {
9899
return constants.JellyfinRegexp.Cache.Subtitle
99100
}
100101

@@ -234,12 +235,26 @@ func (jellyfinHandler *JellyfinHandler) VideosHandler(ctx *gin.Context) {
234235

235236
// 并发控制:确保同一个 item ID 只有一个任务在运行
236237
// 将整个处理流程放在锁内,避免重复查询和重复获取重定向 URL
237-
var mu *sync.Mutex
238+
var muWrapper *mutexWithRefCount
238239
if itemID != "" {
239-
mutex, _ := jellyfinHandler.playbackInfoMutex.LoadOrStore(itemID, &sync.Mutex{})
240-
mu = mutex.(*sync.Mutex)
241-
mu.Lock()
242-
defer mu.Unlock()
240+
// 加载或创建 mutex wrapper
241+
value, _ := jellyfinHandler.playbackInfoMutex.LoadOrStore(itemID, &mutexWithRefCount{})
242+
muWrapper = value.(*mutexWithRefCount)
243+
244+
// 增加引用计数
245+
atomic.AddInt32(&muWrapper.refCount, 1)
246+
247+
// 锁定并处理
248+
muWrapper.mu.Lock()
249+
defer func() {
250+
muWrapper.mu.Unlock()
251+
// 减少引用计数
252+
refCount := atomic.AddInt32(&muWrapper.refCount, -1)
253+
// 如果没有其他 goroutine 在使用,删除这个 mutex 以避免内存泄漏
254+
if refCount == 0 {
255+
jellyfinHandler.playbackInfoMutex.Delete(itemID)
256+
}
257+
}()
243258
logging.Debugf("开始处理 item %s 的 VideosHandler 请求", itemID)
244259
}
245260

0 commit comments

Comments
 (0)