diff --git a/component/components/lockstep.go b/component/components/lockstep.go index eb21c6f..8b40d01 100644 --- a/component/components/lockstep.go +++ b/component/components/lockstep.go @@ -3,7 +3,7 @@ package components import ( "encoding/json" "github.com/kercylan98/minotaur/component" - "github.com/kercylan98/minotaur/utils/synchronization" + "github.com/kercylan98/minotaur/utils/concurrent" "github.com/kercylan98/minotaur/utils/timer" "sync" "sync/atomic" @@ -13,8 +13,8 @@ import ( // NewLockstep 创建一个锁步(帧)同步默认实现的组件(Lockstep)进行返回 func NewLockstep[ClientID comparable, Command any](options ...LockstepOption[ClientID, Command]) *Lockstep[ClientID, Command] { lockstep := &Lockstep[ClientID, Command]{ - clients: synchronization.NewMap[ClientID, component.LockstepClient[ClientID]](), - frames: synchronization.NewMap[int, []Command](), + clients: concurrent.NewBalanceMap[ClientID, component.LockstepClient[ClientID]](), + frames: concurrent.NewBalanceMap[int, []Command](), ticker: timer.GetTicker(10), frameRate: 15, serialization: func(frame int, commands []Command) []byte { @@ -25,7 +25,7 @@ func NewLockstep[ClientID comparable, Command any](options ...LockstepOption[Cli data, _ := json.Marshal(frameStruct) return data }, - clientCurrentFrame: synchronization.NewMap[ClientID, int](), + clientCurrentFrame: concurrent.NewBalanceMap[ClientID, int](), } for _, option := range options { option(lockstep) @@ -40,12 +40,12 @@ func NewLockstep[ClientID comparable, Command any](options ...LockstepOption[Cli // - 从特定帧开始追帧 // - 兼容各种基于TCP/UDP/Unix的网络类型,可通过客户端实现其他网络类型同步 type Lockstep[ClientID comparable, Command any] struct { - clients *synchronization.Map[ClientID, component.LockstepClient[ClientID]] // 接受广播的客户端 - frames *synchronization.Map[int, []Command] // 所有帧指令 - ticker *timer.Ticker // 定时器 - frameMutex sync.Mutex // 帧锁 - currentFrame int // 当前帧 - clientCurrentFrame *synchronization.Map[ClientID, int] // 客户端当前帧数 + clients *concurrent.BalanceMap[ClientID, component.LockstepClient[ClientID]] // 接受广播的客户端 + frames *concurrent.BalanceMap[int, []Command] // 所有帧指令 + ticker *timer.Ticker // 定时器 + frameMutex sync.Mutex // 帧锁 + currentFrame int // 当前帧 + clientCurrentFrame *concurrent.BalanceMap[ClientID, int] // 客户端当前帧数 running atomic.Bool frameRate int // 帧率(每秒N帧) @@ -123,8 +123,8 @@ func (slf *Lockstep[ClientID, Command]) StopBroadcast() { // AddCommand 添加命令到当前帧 func (slf *Lockstep[ClientID, Command]) AddCommand(command Command) { - slf.frames.AtomGetSet(slf.currentFrame, func(value []Command, exist bool) (newValue []Command, isSet bool) { - return append(value, command), true + slf.frames.Atom(func(m map[int][]Command) { + m[slf.currentFrame] = append(m[slf.currentFrame], command) }) } diff --git a/game/builtin/attrs.go b/game/builtin/attrs.go index 04b2fcd..8dc2683 100644 --- a/game/builtin/attrs.go +++ b/game/builtin/attrs.go @@ -2,18 +2,18 @@ package builtin import ( "github.com/kercylan98/minotaur/game" + "github.com/kercylan98/minotaur/utils/concurrent" "github.com/kercylan98/minotaur/utils/huge" - "github.com/kercylan98/minotaur/utils/synchronization" ) func NewAttrs() *Attrs { return &Attrs{ - attrs: synchronization.NewMap[int, any](), + attrs: concurrent.NewBalanceMap[int, any](), } } type Attrs struct { - attrs *synchronization.Map[int, any] + attrs *concurrent.BalanceMap[int, any] attrChangeEventHandles []game.AttrChangeEventHandle attrIdChangeEventHandles map[int][]game.AttrChangeEventHandle diff --git a/game/builtin/ranking_list.go b/game/builtin/ranking_list.go index 9d144e0..e366883 100644 --- a/game/builtin/ranking_list.go +++ b/game/builtin/ranking_list.go @@ -3,15 +3,15 @@ package builtin import ( "encoding/json" "github.com/kercylan98/minotaur/game" + "github.com/kercylan98/minotaur/utils/concurrent" "github.com/kercylan98/minotaur/utils/generic" - "github.com/kercylan98/minotaur/utils/synchronization" ) // NewRankingList 创建一个排名从0开始的排行榜 func NewRankingList[CompetitorID comparable, Score generic.Ordered](options ...RankingListOption[CompetitorID, Score]) *RankingList[CompetitorID, Score] { rankingList := &RankingList[CompetitorID, Score]{ rankCount: 100, - competitors: synchronization.NewMap[CompetitorID, Score](), + competitors: concurrent.NewBalanceMap[CompetitorID, Score](), } for _, option := range options { option(rankingList) @@ -22,7 +22,7 @@ func NewRankingList[CompetitorID comparable, Score generic.Ordered](options ...R type RankingList[CompetitorID comparable, Score generic.Ordered] struct { asc bool rankCount int - competitors *synchronization.Map[CompetitorID, Score] + competitors *concurrent.BalanceMap[CompetitorID, Score] scores []*scoreItem[CompetitorID, Score] // CompetitorID, Score rankChangeEventHandles []game.RankChangeEventHandle[CompetitorID, Score] @@ -244,11 +244,11 @@ func (slf *RankingList[CompetitorID, Score]) competitor(competitorId CompetitorI func (slf *RankingList[CompetitorID, Score]) UnmarshalJSON(bytes []byte) error { var t struct { - Competitors *synchronization.Map[CompetitorID, Score] `json:"competitors,omitempty"` - Scores []*scoreItem[CompetitorID, Score] `json:"scores,omitempty"` - Asc bool `json:"asc,omitempty"` + Competitors *concurrent.BalanceMap[CompetitorID, Score] `json:"competitors,omitempty"` + Scores []*scoreItem[CompetitorID, Score] `json:"scores,omitempty"` + Asc bool `json:"asc,omitempty"` } - t.Competitors = synchronization.NewMap[CompetitorID, Score]() + t.Competitors = concurrent.NewBalanceMap[CompetitorID, Score]() if err := json.Unmarshal(bytes, &t); err != nil { return err } @@ -260,9 +260,9 @@ func (slf *RankingList[CompetitorID, Score]) UnmarshalJSON(bytes []byte) error { func (slf *RankingList[CompetitorID, Score]) MarshalJSON() ([]byte, error) { var t struct { - Competitors *synchronization.Map[CompetitorID, Score] `json:"competitors,omitempty"` - Scores []*scoreItem[CompetitorID, Score] `json:"scores,omitempty"` - Asc bool `json:"asc,omitempty"` + Competitors *concurrent.BalanceMap[CompetitorID, Score] `json:"competitors,omitempty"` + Scores []*scoreItem[CompetitorID, Score] `json:"scores,omitempty"` + Asc bool `json:"asc,omitempty"` } t.Competitors = slf.competitors t.Scores = slf.scores diff --git a/game/builtin/room.go b/game/builtin/room.go index 457d5b0..86aabee 100644 --- a/game/builtin/room.go +++ b/game/builtin/room.go @@ -2,8 +2,7 @@ package builtin import ( "github.com/kercylan98/minotaur/game" - "github.com/kercylan98/minotaur/utils/asynchronous" - "github.com/kercylan98/minotaur/utils/hash" + "github.com/kercylan98/minotaur/utils/concurrent" "github.com/kercylan98/minotaur/utils/log" ) @@ -11,7 +10,7 @@ import ( func NewRoom[PlayerID comparable, Player game.Player[PlayerID]](guid int64, options ...RoomOption[PlayerID, Player]) *Room[PlayerID, Player] { room := &Room[PlayerID, Player]{ guid: guid, - players: asynchronous.NewMap[PlayerID, Player](), + players: concurrent.NewBalanceMap[PlayerID, Player](), } for _, option := range options { option(room) @@ -27,7 +26,7 @@ type Room[PlayerID comparable, Player game.Player[PlayerID]] struct { owner PlayerID noMaster bool playerLimit int - players hash.Map[PlayerID, Player] + players *concurrent.BalanceMap[PlayerID, Player] kickCheckHandle func(room *Room[PlayerID, Player], id, target PlayerID) error playerJoinRoomEventHandles []game.PlayerJoinRoomEventHandle[PlayerID, Player] @@ -51,8 +50,8 @@ func (slf *Room[PlayerID, Player]) GetPlayer(id PlayerID) Player { } // GetPlayers 获取所有玩家 -func (slf *Room[PlayerID, Player]) GetPlayers() hash.MapReadonly[PlayerID, Player] { - return slf.players.(hash.MapReadonly[PlayerID, Player]) +func (slf *Room[PlayerID, Player]) GetPlayers() map[PlayerID]Player { + return slf.players.Map() } // GetPlayerCount 获取玩家数量 diff --git a/game/builtin/room_manager.go b/game/builtin/room_manager.go index bd13d08..86f17c3 100644 --- a/game/builtin/room_manager.go +++ b/game/builtin/room_manager.go @@ -2,20 +2,20 @@ package builtin import ( "github.com/kercylan98/minotaur/game" - "github.com/kercylan98/minotaur/utils/synchronization" + "github.com/kercylan98/minotaur/utils/concurrent" "sync/atomic" ) func NewRoomManager[PlayerID comparable, Room game.Room[PlayerID, game.Player[PlayerID]]]() *RoomManager[PlayerID, Room] { return &RoomManager[PlayerID, Room]{ - rooms: synchronization.NewMap[int64, Room](), + rooms: concurrent.NewBalanceMap[int64, Room](), } } // RoomManager 房间管理器 type RoomManager[PlayerID comparable, Room game.Room[PlayerID, game.Player[PlayerID]]] struct { guid atomic.Int64 - rooms *synchronization.Map[int64, Room] + rooms *concurrent.BalanceMap[int64, Room] } // GenGuid 生成一个新的房间guid diff --git a/game/builtin/room_options.go b/game/builtin/room_options.go index 0b9801d..b67a6ec 100644 --- a/game/builtin/room_options.go +++ b/game/builtin/room_options.go @@ -2,7 +2,7 @@ package builtin import ( "github.com/kercylan98/minotaur/game" - "github.com/kercylan98/minotaur/utils/synchronization" + "github.com/kercylan98/minotaur/utils/concurrent" ) // RoomOption 房间构建可选项 @@ -11,7 +11,7 @@ type RoomOption[PlayerID comparable, Player game.Player[PlayerID]] func(room *Ro // WithRoomSync 通过线程安全的方式创建房间 func WithRoomSync[PlayerID comparable, Player game.Player[PlayerID]]() RoomOption[PlayerID, Player] { return func(room *Room[PlayerID, Player]) { - room.players = synchronization.NewMap[PlayerID, Player]() + room.players = concurrent.NewBalanceMap[PlayerID, Player]() } } diff --git a/game/builtin/room_seat.go b/game/builtin/room_seat.go index 2fc31ea..55ad7f7 100644 --- a/game/builtin/room_seat.go +++ b/game/builtin/room_seat.go @@ -2,7 +2,7 @@ package builtin import ( "github.com/kercylan98/minotaur/game" - "github.com/kercylan98/minotaur/utils/asynchronous" + "github.com/kercylan98/minotaur/utils/concurrent" "github.com/kercylan98/minotaur/utils/hash" "github.com/kercylan98/minotaur/utils/slice" "sync" @@ -12,7 +12,7 @@ import ( func NewRoomSeat[PlayerID comparable, Player game.Player[PlayerID]](room game.Room[PlayerID, Player], options ...RoomSeatOption[PlayerID, Player]) *RoomSeat[PlayerID, Player] { roomSeat := &RoomSeat[PlayerID, Player]{ Room: room, - seatPS: asynchronous.NewMap[PlayerID, int](), + seatPS: concurrent.NewBalanceMap[PlayerID, int](), } for _, option := range options { option(roomSeat) @@ -26,7 +26,7 @@ type RoomSeat[PlayerID comparable, Player game.Player[PlayerID]] struct { game.Room[PlayerID, Player] mutex sync.RWMutex vacancy []int - seatPS hash.Map[PlayerID, int] + seatPS *concurrent.BalanceMap[PlayerID, int] seatSP []*PlayerID duplicateLock bool fillIn bool @@ -240,6 +240,41 @@ func (slf *RoomSeat[PlayerID, Player]) GetNextSeatVacancy(seat int) int { return seat } +// GetPrevSeat 获取特定座位号上一个未缺席的座位号 +func (slf *RoomSeat[PlayerID, Player]) GetPrevSeat(seat int) int { + l := len(slf.seatSP) + if l == 0 || seat >= l || seat < 0 { + return -1 + } + var target = seat + for { + target-- + if target < 0 { + target = l - 1 + } + if target == seat { + return seat + } + if slf.seatSP[target] != nil { + return target + } + } +} + +// GetPrevSeatVacancy 获取特定座位号上一个座位号 +// - 缺席将不会被忽略 +func (slf *RoomSeat[PlayerID, Player]) GetPrevSeatVacancy(seat int) int { + l := len(slf.seatSP) + if l == 0 || seat >= l || seat < 0 { + return -1 + } + seat-- + if seat < 0 { + seat = l - 1 + } + return seat +} + func (slf *RoomSeat[PlayerID, Player]) onJoinRoom(room game.Room[PlayerID, Player], player Player) { slf.AddSeat(player.GetID()) } diff --git a/game/builtin/world.go b/game/builtin/world.go index 463b76d..f86e794 100644 --- a/game/builtin/world.go +++ b/game/builtin/world.go @@ -2,9 +2,8 @@ package builtin import ( "github.com/kercylan98/minotaur/game" - "github.com/kercylan98/minotaur/utils/hash" + "github.com/kercylan98/minotaur/utils/concurrent" "github.com/kercylan98/minotaur/utils/log" - "github.com/kercylan98/minotaur/utils/synchronization" "sync/atomic" ) @@ -12,10 +11,10 @@ import ( func NewWorld[PlayerID comparable, Player game.Player[PlayerID]](guid int64, options ...WorldOption[PlayerID, Player]) *World[PlayerID, Player] { world := &World[PlayerID, Player]{ guid: guid, - players: synchronization.NewMap[PlayerID, Player](), - playerActors: synchronization.NewMap[PlayerID, hash.Map[int64, game.Actor]](), - owners: synchronization.NewMap[int64, PlayerID](), - actors: synchronization.NewMap[int64, game.Actor](), + players: concurrent.NewBalanceMap[PlayerID, Player](), + playerActors: concurrent.NewBalanceMap[PlayerID, *concurrent.BalanceMap[int64, game.Actor]](), + owners: concurrent.NewBalanceMap[int64, PlayerID](), + actors: concurrent.NewBalanceMap[int64, game.Actor](), } for _, option := range options { option(world) @@ -28,10 +27,10 @@ type World[PlayerID comparable, Player game.Player[PlayerID]] struct { guid int64 actorGuid atomic.Int64 playerLimit int - players hash.Map[PlayerID, Player] - playerActors hash.Map[PlayerID, hash.Map[int64, game.Actor]] - owners hash.Map[int64, PlayerID] - actors hash.Map[int64, game.Actor] + players *concurrent.BalanceMap[PlayerID, Player] + playerActors *concurrent.BalanceMap[PlayerID, *concurrent.BalanceMap[int64, game.Actor]] + owners *concurrent.BalanceMap[int64, PlayerID] + actors *concurrent.BalanceMap[int64, game.Actor] playerJoinWorldEventHandles []game.PlayerJoinWorldEventHandle[PlayerID, Player] playerLeaveWorldEventHandles []game.PlayerLeaveWorldEventHandle[PlayerID, Player] @@ -56,16 +55,16 @@ func (slf *World[PlayerID, Player]) GetPlayer(id PlayerID) Player { return slf.players.Get(id) } -func (slf *World[PlayerID, Player]) GetPlayers() hash.MapReadonly[PlayerID, Player] { - return slf.players.(hash.MapReadonly[PlayerID, Player]) +func (slf *World[PlayerID, Player]) GetPlayers() map[PlayerID]Player { + return slf.players.Map() } func (slf *World[PlayerID, Player]) GetActor(guid int64) game.Actor { return slf.actors.Get(guid) } -func (slf *World[PlayerID, Player]) GetActors() hash.MapReadonly[int64, game.Actor] { - return slf.actors.(hash.MapReadonly[int64, game.Actor]) +func (slf *World[PlayerID, Player]) GetActors() map[int64]game.Actor { + return slf.actors.Map() } func (slf *World[PlayerID, Player]) GetPlayerActor(id PlayerID, guid int64) game.Actor { @@ -75,8 +74,8 @@ func (slf *World[PlayerID, Player]) GetPlayerActor(id PlayerID, guid int64) game return nil } -func (slf *World[PlayerID, Player]) GetPlayerActors(id PlayerID) hash.MapReadonly[int64, game.Actor] { - return slf.playerActors.Get(id).(hash.MapReadonly[int64, game.Actor]) +func (slf *World[PlayerID, Player]) GetPlayerActors(id PlayerID) map[int64]game.Actor { + return slf.playerActors.Get(id).Map() } func (slf *World[PlayerID, Player]) IsExistPlayer(id PlayerID) bool { @@ -105,7 +104,7 @@ func (slf *World[PlayerID, Player]) Join(player Player) error { log.Debug("World.Join", log.Int64("guid", slf.GetGuid()), log.Any("player", player.GetID())) slf.players.Set(player.GetID(), player) if actors := slf.playerActors.Get(player.GetID()); actors == nil { - actors = synchronization.NewMap[int64, game.Actor]() + actors = concurrent.NewBalanceMap[int64, game.Actor]() slf.playerActors.Set(player.GetID(), actors) } slf.OnPlayerJoinWorldEvent(player) @@ -119,9 +118,10 @@ func (slf *World[PlayerID, Player]) Leave(id PlayerID) { } log.Debug("World.Leave", log.Int64("guid", slf.GetGuid()), log.Any("player", player.GetID())) slf.OnPlayerLeaveWorldEvent(player) - slf.playerActors.Get(player.GetID()).Range(func(guid int64, actor game.Actor) { + slf.playerActors.Get(player.GetID()).Range(func(guid int64, actor game.Actor) bool { slf.OnActorAnnihilationEvent(actor) slf.owners.Delete(guid) + return false }) slf.playerActors.Delete(player.GetID()) slf.players.Delete(player.GetID()) @@ -171,8 +171,9 @@ func (slf *World[PlayerID, Player]) RemoveActorOwner(guid int64) { func (slf *World[PlayerID, Player]) Reset() { log.Debug("World", log.Int64("Reset", slf.guid)) slf.players.Clear() - slf.playerActors.Range(func(id PlayerID, actors hash.Map[int64, game.Actor]) { + slf.playerActors.Range(func(id PlayerID, actors *concurrent.BalanceMap[int64, game.Actor]) bool { actors.Clear() + return false }) slf.playerActors.Clear() slf.owners.Clear() diff --git a/game/poker/poker.go b/game/poker/poker.go index c575bb2..b59fdf1 100644 --- a/game/poker/poker.go +++ b/game/poker/poker.go @@ -68,3 +68,23 @@ func GetCardsColor(cards ...Card) []Color { } return colors } + +// IsContain 一组扑克牌是否包含某张牌 +func IsContain(cards []Card, card Card) bool { + for _, c := range cards { + if c.Equal(card) { + return true + } + } + return false +} + +// IsContainAll 一组扑克牌是否包含另一组扑克牌 +func IsContainAll(cards []Card, cards2 []Card) bool { + for _, card := range cards2 { + if !IsContain(cards, card) { + return false + } + } + return true +} diff --git a/game/room.go b/game/room.go index 275ad8b..ed3bb3d 100644 --- a/game/room.go +++ b/game/room.go @@ -1,9 +1,5 @@ package game -import ( - "github.com/kercylan98/minotaur/utils/hash" -) - // Room 房间类似于简版的游戏世界(World),不过没有游戏实体 type Room[PlayerID comparable, P Player[PlayerID]] interface { // GetGuid 获取房间的唯一标识符 @@ -13,7 +9,7 @@ type Room[PlayerID comparable, P Player[PlayerID]] interface { // GetPlayer 根据玩家id获取玩家 GetPlayer(id PlayerID) P // GetPlayers 获取房间中的所有玩家 - GetPlayers() hash.MapReadonly[PlayerID, P] + GetPlayers() map[PlayerID]P // GetPlayerCount 获取玩家数量 GetPlayerCount() int // IsExistPlayer 检查房间中是否存在特定玩家 diff --git a/game/room_seat.go b/game/room_seat.go index 05cb24c..8814417 100644 --- a/game/room_seat.go +++ b/game/room_seat.go @@ -36,4 +36,12 @@ type RoomSeat[PlayerID comparable, P Player[PlayerID]] interface { // - 超出范围将返回-1 // - 当没有下一个座位号时将始终返回本身 GetNextSeatVacancy(seat int) int + // GetPrevSeat 获取上一个座位号,空缺的位置将会被跳过 + // - 超出范围将返回-1 + // - 当没有上一个座位号时将始终返回本身 + GetPrevSeat(seat int) int + // GetPrevSeatVacancy 获取上一个座位号,空缺的位置将被保留 + // - 超出范围将返回-1 + // - 当没有上一个座位号时将始终返回本身 + GetPrevSeatVacancy(seat int) int } diff --git a/game/world.go b/game/world.go index ba2a811..82b1399 100644 --- a/game/world.go +++ b/game/world.go @@ -1,9 +1,5 @@ package game -import ( - "github.com/kercylan98/minotaur/utils/hash" -) - // World 游戏世界接口定义 type World[PlayerID comparable, P Player[PlayerID]] interface { // GetGuid 获取世界的唯一标识符 @@ -13,15 +9,15 @@ type World[PlayerID comparable, P Player[PlayerID]] interface { // GetPlayer 根据玩家id获取玩家 GetPlayer(id PlayerID) P // GetPlayers 获取世界中的所有玩家 - GetPlayers() hash.MapReadonly[PlayerID, P] + GetPlayers() map[PlayerID]P // GetActor 根据唯一标识符获取世界中的游戏对象 GetActor(guid int64) Actor // GetActors 获取世界中的所有游戏对象 - GetActors() hash.MapReadonly[int64, Actor] + GetActors() map[int64]Actor // GetPlayerActor 获取游戏世界中归属特定玩家的特定游戏对象 GetPlayerActor(id PlayerID, guid int64) Actor // GetPlayerActors 获取游戏世界中归属特定玩家的所有游戏对象 - GetPlayerActors(id PlayerID) hash.MapReadonly[int64, Actor] + GetPlayerActors(id PlayerID) map[int64]Actor // IsExistPlayer 检查游戏世界中是否存在特定玩家 IsExistPlayer(id PlayerID) bool // IsExistActor 检查游戏世界中是否存在特定游戏对象 diff --git a/go.mod b/go.mod index 83a3804..bde4afb 100644 --- a/go.mod +++ b/go.mod @@ -23,6 +23,7 @@ require ( ) require ( + github.com/alphadose/haxmap v1.2.0 // indirect github.com/bytedance/sonic v1.9.1 // indirect github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect github.com/gabriel-vasile/mimetype v1.4.2 // indirect @@ -61,6 +62,7 @@ require ( go.uber.org/multierr v1.11.0 // indirect golang.org/x/arch v0.3.0 // indirect golang.org/x/crypto v0.9.0 // indirect + golang.org/x/exp v0.0.0-20221031165847-c99f073a8326 // indirect golang.org/x/net v0.10.0 // indirect golang.org/x/sys v0.8.0 // indirect golang.org/x/text v0.9.0 // indirect diff --git a/go.sum b/go.sum index ec55401..6f9d86e 100644 --- a/go.sum +++ b/go.sum @@ -2,6 +2,8 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/RussellLuo/timingwheel v0.0.0-20220218152713-54845bda3108 h1:iPugyBI7oFtbDZXC4dnY093M1kZx6k/95sen92gafbY= github.com/RussellLuo/timingwheel v0.0.0-20220218152713-54845bda3108/go.mod h1:WAMLHwunr1hi3u7OjGV6/VWG9QbdMhGpEKjROiSFd10= +github.com/alphadose/haxmap v1.2.0 h1:noGrAmCE+gNheZ4KpW+sYj9W5uMcO1UAjbAq9XBOAfM= +github.com/alphadose/haxmap v1.2.0/go.mod h1:rjHw1IAqbxm0S3U5tD16GoKsiAd8FWx5BJ2IYqXwgmM= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= @@ -219,6 +221,8 @@ golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20221031165847-c99f073a8326 h1:QfTh0HpN6hlw6D3vu8DAwC8pBIwikq0AI1evdm+FksE= +golang.org/x/exp v0.0.0-20221031165847-c99f073a8326/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= diff --git a/server/conn.go b/server/conn.go index 1d0fd69..0c7d347 100644 --- a/server/conn.go +++ b/server/conn.go @@ -2,7 +2,7 @@ package server import ( "github.com/gorilla/websocket" - "github.com/kercylan98/minotaur/utils/synchronization" + "github.com/kercylan98/minotaur/utils/concurrent" "github.com/panjf2000/gnet" "github.com/xtaci/kcp-go/v5" "net" @@ -75,7 +75,7 @@ type Conn struct { kcp *kcp.UDPSession data map[any]any mutex sync.Mutex - packetPool *synchronization.Pool[*connPacket] + packetPool *concurrent.Pool[*connPacket] packets []*connPacket } @@ -187,7 +187,7 @@ func (slf *Conn) WriteWithCallback(packet Packet, callback func(err error), mess // writeLoop 写循环 func (slf *Conn) writeLoop(wait *sync.WaitGroup) { - slf.packetPool = synchronization.NewPool[*connPacket](10*1024, + slf.packetPool = concurrent.NewPool[*connPacket](10*1024, func() *connPacket { return &connPacket{} }, func(data *connPacket) { diff --git a/server/cross/nats.go b/server/cross/nats.go index 8b13d21..d27c80c 100644 --- a/server/cross/nats.go +++ b/server/cross/nats.go @@ -4,8 +4,8 @@ import ( "encoding/json" "fmt" "github.com/kercylan98/minotaur/server" + "github.com/kercylan98/minotaur/utils/concurrent" "github.com/kercylan98/minotaur/utils/log" - "github.com/kercylan98/minotaur/utils/synchronization" "github.com/nats-io/nats.go" "time" ) @@ -18,7 +18,7 @@ func NewNats(url string, options ...NatsOption) *Nats { n := &Nats{ url: url, subject: "MINOTAUR_CROSS", - messagePool: synchronization.NewPool[*Message](1024*100, func() *Message { + messagePool: concurrent.NewPool[*Message](1024*100, func() *Message { return new(Message) }, func(data *Message) { data.ServerId = 0 @@ -36,7 +36,7 @@ type Nats struct { url string subject string options []nats.Option - messagePool *synchronization.Pool[*Message] + messagePool *concurrent.Pool[*Message] } func (slf *Nats) Init(server *server.Server, packetHandle func(serverId int64, packet []byte)) (err error) { diff --git a/server/server.go b/server/server.go index 210bc2e..e400f16 100644 --- a/server/server.go +++ b/server/server.go @@ -5,9 +5,9 @@ import ( "fmt" "github.com/gin-gonic/gin" "github.com/gorilla/websocket" + "github.com/kercylan98/minotaur/utils/concurrent" "github.com/kercylan98/minotaur/utils/log" "github.com/kercylan98/minotaur/utils/super" - "github.com/kercylan98/minotaur/utils/synchronization" "github.com/kercylan98/minotaur/utils/timer" "github.com/kercylan98/minotaur/utils/times" "github.com/panjf2000/ants/v2" @@ -33,7 +33,7 @@ func New(network Network, options ...Option) *Server { runtime: &runtime{messagePoolSize: DefaultMessageBufferSize, messageChannelSize: DefaultMessageChannelSize}, option: &option{}, network: network, - online: synchronization.NewMap[string, *Conn](), + online: concurrent.NewBalanceMap[string, *Conn](), closeChannel: make(chan struct{}, 1), systemSignal: make(chan os.Signal, 1), } @@ -72,26 +72,26 @@ func New(network Network, options ...Option) *Server { // Server 网络服务器 type Server struct { - *event // 事件 - *runtime // 运行时 - *option // 可选项 - network Network // 网络类型 - addr string // 侦听地址 - systemSignal chan os.Signal // 系统信号 - online *synchronization.Map[string, *Conn] // 在线连接 - ginServer *gin.Engine // HTTP模式下的路由器 - httpServer *http.Server // HTTP模式下的服务器 - grpcServer *grpc.Server // GRPC模式下的服务器 - gServer *gNet // TCP或UDP模式下的服务器 - isRunning bool // 是否正在运行 - isShutdown atomic.Bool // 是否已关闭 - closeChannel chan struct{} // 关闭信号 - ants *ants.Pool // 协程池 - messagePool *synchronization.Pool[*Message] // 消息池 - messageChannel chan *Message // 消息管道 - multiple *MultipleServer // 多服务器模式下的服务器 - multipleRuntimeErrorChan chan error // 多服务器模式下的运行时错误 - runMode RunMode // 运行模式 + *event // 事件 + *runtime // 运行时 + *option // 可选项 + network Network // 网络类型 + addr string // 侦听地址 + systemSignal chan os.Signal // 系统信号 + online *concurrent.BalanceMap[string, *Conn] // 在线连接 + ginServer *gin.Engine // HTTP模式下的路由器 + httpServer *http.Server // HTTP模式下的服务器 + grpcServer *grpc.Server // GRPC模式下的服务器 + gServer *gNet // TCP或UDP模式下的服务器 + isRunning bool // 是否正在运行 + isShutdown atomic.Bool // 是否已关闭 + closeChannel chan struct{} // 关闭信号 + ants *ants.Pool // 协程池 + messagePool *concurrent.Pool[*Message] // 消息池 + messageChannel chan *Message // 消息管道 + multiple *MultipleServer // 多服务器模式下的服务器 + multipleRuntimeErrorChan chan error // 多服务器模式下的运行时错误 + runMode RunMode // 运行模式 } // Run 使用特定地址运行服务器 @@ -115,7 +115,7 @@ func (slf *Server) Run(addr string) error { var protoAddr = fmt.Sprintf("%s://%s", slf.network, slf.addr) var messageInitFinish = make(chan struct{}, 1) var connectionInitHandle = func(callback func()) { - slf.messagePool = synchronization.NewPool[*Message](slf.messagePoolSize, + slf.messagePool = concurrent.NewPool[*Message](slf.messagePoolSize, func() *Message { return &Message{} }, diff --git a/utils/asynchronous/map.go b/utils/asynchronous/map.go deleted file mode 100644 index 36524a4..0000000 --- a/utils/asynchronous/map.go +++ /dev/null @@ -1,195 +0,0 @@ -package asynchronous - -import ( - "encoding/json" - "github.com/kercylan98/minotaur/utils/hash" -) - -func NewMap[Key comparable, Value any](options ...MapOption[Key, Value]) *Map[Key, Value] { - m := &Map[Key, Value]{ - data: make(map[Key]Value), - } - for _, option := range options { - option(m) - } - return m -} - -// Map 非并发安全的字典数据结构 -// - 可用于对 synchronization.Map 的替代 -type Map[Key comparable, Value any] struct { - data map[Key]Value -} - -func (slf *Map[Key, Value]) Set(key Key, value Value) { - slf.data[key] = value -} - -func (slf *Map[Key, Value]) Get(key Key) Value { - return slf.data[key] -} - -// AtomGetSet 原子方式获取一个值并在之后进行赋值 -func (slf *Map[Key, Value]) AtomGetSet(key Key, handle func(value Value, exist bool) (newValue Value, isSet bool)) { - value, exist := slf.data[key] - if newValue, isSet := handle(value, exist); isSet { - slf.data[key] = newValue - } -} - -// Atom 原子操作 -func (slf *Map[Key, Value]) Atom(handle func(m hash.Map[Key, Value])) { - handle(slf) -} - -func (slf *Map[Key, Value]) Exist(key Key) bool { - _, exist := slf.data[key] - return exist -} - -func (slf *Map[Key, Value]) GetExist(key Key) (Value, bool) { - value, exist := slf.data[key] - return value, exist -} - -func (slf *Map[Key, Value]) Delete(key Key) { - delete(slf.data, key) -} - -func (slf *Map[Key, Value]) DeleteGet(key Key) Value { - v := slf.data[key] - delete(slf.data, key) - return v -} - -func (slf *Map[Key, Value]) DeleteGetExist(key Key) (Value, bool) { - v, exist := slf.data[key] - delete(slf.data, key) - return v, exist -} - -func (slf *Map[Key, Value]) DeleteExist(key Key) bool { - if _, exist := slf.data[key]; !exist { - return exist - } - delete(slf.data, key) - return true -} - -func (slf *Map[Key, Value]) Clear() { - for k := range slf.data { - delete(slf.data, k) - } -} - -func (slf *Map[Key, Value]) ClearHandle(handle func(key Key, value Value)) { - for k, v := range slf.data { - handle(k, v) - delete(slf.data, k) - } -} - -func (slf *Map[Key, Value]) Range(handle func(key Key, value Value)) { - for k, v := range slf.data { - key, value := k, v - handle(key, value) - } -} - -func (slf *Map[Key, Value]) RangeSkip(handle func(key Key, value Value) bool) { - for k, v := range slf.data { - key, value := k, v - if !handle(key, value) { - continue - } - } -} - -func (slf *Map[Key, Value]) RangeBreakout(handle func(key Key, value Value) bool) { - slf.rangeBreakout(handle) -} - -func (slf *Map[Key, Value]) rangeBreakout(handle func(key Key, value Value) bool) bool { - for k, v := range slf.data { - key, value := k, v - if !handle(key, value) { - return true - } - } - return false -} - -func (slf *Map[Key, Value]) RangeFree(handle func(key Key, value Value, skip func(), breakout func())) { - slf.rangeFree(handle) -} - -func (slf *Map[Key, Value]) rangeFree(handle func(key Key, value Value, skip func(), breakout func())) bool { - var skipExec, breakoutExec bool - var skip = func() { - skipExec = true - } - var breakout = func() { - breakoutExec = true - } - for k, v := range slf.data { - key, value := k, v - handle(key, value, skip, breakout) - if skipExec { - continue - } - if breakoutExec { - break - } - } - return breakoutExec -} - -func (slf *Map[Key, Value]) Keys() []Key { - var s = make([]Key, 0, len(slf.data)) - for k := range slf.data { - s = append(s, k) - } - return s -} - -func (slf *Map[Key, Value]) Slice() []Value { - var s = make([]Value, 0, len(slf.data)) - for _, v := range slf.data { - s = append(s, v) - } - return s -} - -func (slf *Map[Key, Value]) Map() map[Key]Value { - var m = make(map[Key]Value) - for k, v := range slf.data { - m[k] = v - } - return m -} - -func (slf *Map[Key, Value]) Size() int { - return len(slf.data) -} - -// GetOne 获取一个 -func (slf *Map[Key, Value]) GetOne() (value Value) { - for _, v := range slf.data { - return v - } - return value -} - -func (slf *Map[Key, Value]) MarshalJSON() ([]byte, error) { - m := slf.Map() - return json.Marshal(m) -} - -func (slf *Map[Key, Value]) UnmarshalJSON(bytes []byte) error { - var m = make(map[Key]Value) - if err := json.Unmarshal(bytes, &m); err != nil { - return err - } - slf.data = m - return nil -} diff --git a/utils/asynchronous/map_options.go b/utils/asynchronous/map_options.go deleted file mode 100644 index 9e4284c..0000000 --- a/utils/asynchronous/map_options.go +++ /dev/null @@ -1,10 +0,0 @@ -package asynchronous - -type MapOption[Key comparable, Value any] func(m *Map[Key, Value]) - -// WithMapSource 通过传入的 map 初始化 -func WithMapSource[Key comparable, Value any](source map[Key]Value) MapOption[Key, Value] { - return func(m *Map[Key, Value]) { - m.data = source - } -} diff --git a/utils/concurrent/balance_map.go b/utils/concurrent/balance_map.go new file mode 100644 index 0000000..39f07ca --- /dev/null +++ b/utils/concurrent/balance_map.go @@ -0,0 +1,185 @@ +package concurrent + +import ( + "encoding/json" + "sync" +) + +// NewBalanceMap 创建一个并发安全且性能在普通读写和并发读写的情况下较为平衡的字典数据结构 +func NewBalanceMap[Key comparable, value any](options ...BalanceMapOption[Key, value]) *BalanceMap[Key, value] { + m := &BalanceMap[Key, value]{ + data: make(map[Key]value), + } + for _, option := range options { + option(m) + } + return m +} + +// BalanceMap 并发安全且性能在普通读写和并发读写的情况下较为平衡的字典数据结构 +// - 适用于要考虑并发读写但是并发读写的频率不高的情况 +type BalanceMap[Key comparable, Value any] struct { + lock sync.RWMutex + data map[Key]Value +} + +// Set 设置一个值 +func (slf *BalanceMap[Key, Value]) Set(key Key, value Value) { + slf.lock.Lock() + defer slf.lock.Unlock() + slf.data[key] = value +} + +// Get 获取一个值 +func (slf *BalanceMap[Key, Value]) Get(key Key) Value { + slf.lock.RLock() + defer slf.lock.RUnlock() + return slf.data[key] +} + +// Atom 原子操作 +func (slf *BalanceMap[Key, Value]) Atom(handle func(m map[Key]Value)) { + slf.lock.Lock() + handle(slf.data) + slf.lock.Unlock() +} + +// Exist 判断是否存在 +func (slf *BalanceMap[Key, Value]) Exist(key Key) bool { + slf.lock.RLock() + _, exist := slf.data[key] + slf.lock.RUnlock() + return exist +} + +// GetExist 获取一个值并判断是否存在 +func (slf *BalanceMap[Key, Value]) GetExist(key Key) (Value, bool) { + slf.lock.RLock() + value, exist := slf.data[key] + slf.lock.RUnlock() + return value, exist +} + +// Delete 删除一个值 +func (slf *BalanceMap[Key, Value]) Delete(key Key) { + slf.lock.Lock() + delete(slf.data, key) + defer slf.lock.Unlock() +} + +// DeleteGet 删除一个值并返回 +func (slf *BalanceMap[Key, Value]) DeleteGet(key Key) Value { + slf.lock.Lock() + v := slf.data[key] + delete(slf.data, key) + slf.lock.Unlock() + return v +} + +// DeleteGetExist 删除一个值并返回是否存在 +func (slf *BalanceMap[Key, Value]) DeleteGetExist(key Key) (Value, bool) { + slf.lock.Lock() + v, exist := slf.data[key] + delete(slf.data, key) + defer slf.lock.Unlock() + return v, exist +} + +// DeleteExist 删除一个值并返回是否存在 +func (slf *BalanceMap[Key, Value]) DeleteExist(key Key) bool { + slf.lock.Lock() + if _, exist := slf.data[key]; !exist { + slf.lock.Unlock() + return exist + } + delete(slf.data, key) + slf.lock.Unlock() + return true +} + +// Clear 清空 +func (slf *BalanceMap[Key, Value]) Clear() { + slf.lock.Lock() + for k := range slf.data { + delete(slf.data, k) + } + slf.lock.Unlock() +} + +// ClearHandle 清空并处理 +func (slf *BalanceMap[Key, Value]) ClearHandle(handle func(key Key, value Value)) { + slf.lock.Lock() + for k, v := range slf.data { + handle(k, v) + delete(slf.data, k) + } + slf.lock.Unlock() +} + +// Range 遍历所有值,如果 handle 返回 true 则停止遍历 +func (slf *BalanceMap[Key, Value]) Range(handle func(key Key, value Value) bool) { + slf.lock.Lock() + for k, v := range slf.data { + key, value := k, v + if handle(key, value) { + break + } + } + slf.lock.Unlock() +} + +// Keys 获取所有的键 +func (slf *BalanceMap[Key, Value]) Keys() []Key { + slf.lock.RLock() + var s = make([]Key, 0, len(slf.data)) + for k := range slf.data { + s = append(s, k) + } + slf.lock.RUnlock() + return s +} + +// Slice 获取所有的值 +func (slf *BalanceMap[Key, Value]) Slice() []Value { + slf.lock.RLock() + var s = make([]Value, 0, len(slf.data)) + for _, v := range slf.data { + s = append(s, v) + } + slf.lock.RUnlock() + return s +} + +// Map 转换为普通 map +func (slf *BalanceMap[Key, Value]) Map() map[Key]Value { + slf.lock.RLock() + var m = make(map[Key]Value) + for k, v := range slf.data { + m[k] = v + } + slf.lock.RUnlock() + return m +} + +// Size 获取数量 +func (slf *BalanceMap[Key, Value]) Size() int { + slf.lock.RLock() + defer slf.lock.RUnlock() + return len(slf.data) +} + +func (slf *BalanceMap[Key, Value]) MarshalJSON() ([]byte, error) { + m := slf.Map() + return json.Marshal(m) +} + +func (slf *BalanceMap[Key, Value]) UnmarshalJSON(bytes []byte) error { + var m = make(map[Key]Value) + if err := json.Unmarshal(bytes, &m); err != nil { + return err + } + slf.lock.Lock() + slf.data = m + slf.lock.Unlock() + return nil +} diff --git a/utils/concurrent/balance_map_options.go b/utils/concurrent/balance_map_options.go new file mode 100644 index 0000000..f49dc6b --- /dev/null +++ b/utils/concurrent/balance_map_options.go @@ -0,0 +1,11 @@ +package concurrent + +// BalanceMapOption BalanceMap 的选项 +type BalanceMapOption[Key comparable, Value any] func(m *BalanceMap[Key, Value]) + +// WithBalanceMapSource 通过传入的 map 初始化 +func WithBalanceMapSource[Key comparable, Value any](source map[Key]Value) BalanceMapOption[Key, Value] { + return func(m *BalanceMap[Key, Value]) { + m.data = source + } +} diff --git a/utils/synchronization/pool.go b/utils/concurrent/pool.go similarity index 98% rename from utils/synchronization/pool.go rename to utils/concurrent/pool.go index 9b2ed24..8f69150 100644 --- a/utils/synchronization/pool.go +++ b/utils/concurrent/pool.go @@ -1,4 +1,4 @@ -package synchronization +package concurrent import ( "github.com/kercylan98/minotaur/utils/log" diff --git a/utils/synchronization/slice.go b/utils/concurrent/slice.go similarity index 97% rename from utils/synchronization/slice.go rename to utils/concurrent/slice.go index 4362615..ac70601 100644 --- a/utils/synchronization/slice.go +++ b/utils/concurrent/slice.go @@ -1,4 +1,4 @@ -package synchronization +package concurrent import ( "github.com/kercylan98/minotaur/utils/slice" diff --git a/utils/synchronization/slice_option.go b/utils/concurrent/slice_option.go similarity index 94% rename from utils/synchronization/slice_option.go rename to utils/concurrent/slice_option.go index ce6fa13..e6b288a 100644 --- a/utils/synchronization/slice_option.go +++ b/utils/concurrent/slice_option.go @@ -1,4 +1,4 @@ -package synchronization +package concurrent type SliceOption[T any] func(slice *Slice[T]) diff --git a/utils/hash/map.go b/utils/hash/map.go deleted file mode 100644 index acb79c7..0000000 --- a/utils/hash/map.go +++ /dev/null @@ -1,29 +0,0 @@ -package hash - -// Map 提供了map集合接口 -type Map[Key comparable, Value any] interface { - Set(key Key, value Value) - Get(key Key) Value - // AtomGetSet 原子方式获取一个值并在之后进行赋值 - AtomGetSet(key Key, handle func(value Value, exist bool) (newValue Value, isSet bool)) - // Atom 原子操作 - Atom(handle func(m Map[Key, Value])) - Exist(key Key) bool - GetExist(key Key) (Value, bool) - Delete(key Key) - DeleteGet(key Key) Value - DeleteGetExist(key Key) (Value, bool) - DeleteExist(key Key) bool - Clear() - ClearHandle(handle func(key Key, value Value)) - Range(handle func(key Key, value Value)) - RangeSkip(handle func(key Key, value Value) bool) - RangeBreakout(handle func(key Key, value Value) bool) - RangeFree(handle func(key Key, value Value, skip func(), breakout func())) - Keys() []Key - Slice() []Value - Map() map[Key]Value - Size() int - // GetOne 获取一个 - GetOne() (value Value) -} diff --git a/utils/hash/map_readonly.go b/utils/hash/map_readonly.go deleted file mode 100644 index 67a442d..0000000 --- a/utils/hash/map_readonly.go +++ /dev/null @@ -1,17 +0,0 @@ -package hash - -// MapReadonly 只读字典接口 -type MapReadonly[Key comparable, Value any] interface { - Get(key Key) Value - Exist(key Key) bool - GetExist(key Key) (Value, bool) - Range(handle func(key Key, value Value)) - RangeSkip(handle func(key Key, value Value) bool) - RangeBreakout(handle func(key Key, value Value) bool) - RangeFree(handle func(key Key, value Value, skip func(), breakout func())) - Keys() []Key - Slice() []Value - Map() map[Key]Value - Size() int - GetOne() (value Value) -} diff --git a/utils/stream/map.go b/utils/stream/map.go index 1495c98..a770933 100644 --- a/utils/stream/map.go +++ b/utils/stream/map.go @@ -1,9 +1,8 @@ package stream import ( - "github.com/kercylan98/minotaur/utils/asynchronous" + "github.com/kercylan98/minotaur/utils/concurrent" "github.com/kercylan98/minotaur/utils/hash" - "github.com/kercylan98/minotaur/utils/synchronization" "reflect" ) @@ -19,11 +18,6 @@ func WithMapCopy[K comparable, V any](m map[K]V) Map[K, V] { return hash.Copy(m) } -// WithHashMap 使用传入的 map 执行链式操作 -func WithHashMap[K comparable, V any](m hash.Map[K, V]) Map[K, V] { - return m.Map() -} - // Map 提供了 map 的链式操作 type Map[K comparable, V any] map[K]V @@ -180,14 +174,9 @@ func (slf Map[K, V]) ToSliceStreamWithKey() Slice[K] { return hash.KeyToSlice(slf) } -// ToSyncMap 将当前 Map 转换为 synchronization.Map -func (slf Map[K, V]) ToSyncMap() *synchronization.Map[K, V] { - return synchronization.NewMap[K, V](synchronization.WithMapSource(slf)) -} - -// ToAsyncMap 将当前 Map 转换为 asynchronous.Map -func (slf Map[K, V]) ToAsyncMap() *asynchronous.Map[K, V] { - return asynchronous.NewMap[K, V](asynchronous.WithMapSource(slf)) +// ToSyncMap 将当前 Map 转换为 concurrent.BalanceMap +func (slf Map[K, V]) ToSyncMap() *concurrent.BalanceMap[K, V] { + return concurrent.NewBalanceMap[K, V](concurrent.WithBalanceMapSource(slf)) } // ToMap 将当前 Map 转换为 map diff --git a/utils/synchronization/map.go b/utils/synchronization/map.go deleted file mode 100644 index f1b0974..0000000 --- a/utils/synchronization/map.go +++ /dev/null @@ -1,285 +0,0 @@ -package synchronization - -import ( - "encoding/json" - "github.com/kercylan98/minotaur/utils/hash" - "sync" -) - -func NewMap[Key comparable, value any](options ...MapOption[Key, value]) *Map[Key, value] { - m := &Map[Key, value]{ - data: make(map[Key]value), - } - for _, option := range options { - option(m) - } - return m -} - -// Map 并发安全的字典数据结构 -type Map[Key comparable, Value any] struct { - lock sync.RWMutex - data map[Key]Value - atom bool -} - -func (slf *Map[Key, Value]) Set(key Key, value Value) { - if !slf.atom { - slf.lock.Lock() - defer slf.lock.Unlock() - } - slf.data[key] = value -} - -func (slf *Map[Key, Value]) Get(key Key) Value { - if !slf.atom { - slf.lock.RLock() - defer slf.lock.RUnlock() - } - return slf.data[key] -} - -// AtomGetSet 原子方式获取一个值并在之后进行赋值 -func (slf *Map[Key, Value]) AtomGetSet(key Key, handle func(value Value, exist bool) (newValue Value, isSet bool)) { - if !slf.atom { - slf.lock.Lock() - defer slf.lock.Unlock() - } - value, exist := slf.data[key] - if newValue, isSet := handle(value, exist); isSet { - slf.data[key] = newValue - } -} - -// Atom 原子操作 -func (slf *Map[Key, Value]) Atom(handle func(m hash.Map[Key, Value])) { - slf.lock.Lock() - slf.atom = true - handle(slf) - slf.atom = false - slf.lock.Unlock() -} - -func (slf *Map[Key, Value]) Exist(key Key) bool { - if !slf.atom { - slf.lock.RLock() - defer slf.lock.RUnlock() - } - _, exist := slf.data[key] - return exist -} - -func (slf *Map[Key, Value]) GetExist(key Key) (Value, bool) { - if !slf.atom { - slf.lock.RLock() - defer slf.lock.RUnlock() - } - value, exist := slf.data[key] - return value, exist -} - -func (slf *Map[Key, Value]) Delete(key Key) { - if !slf.atom { - slf.lock.Lock() - defer slf.lock.Unlock() - } - delete(slf.data, key) -} - -func (slf *Map[Key, Value]) DeleteGet(key Key) Value { - if !slf.atom { - slf.lock.Lock() - defer slf.lock.Unlock() - } - v := slf.data[key] - delete(slf.data, key) - return v -} - -func (slf *Map[Key, Value]) DeleteGetExist(key Key) (Value, bool) { - if !slf.atom { - slf.lock.Lock() - defer slf.lock.Unlock() - } - v, exist := slf.data[key] - delete(slf.data, key) - return v, exist -} - -func (slf *Map[Key, Value]) DeleteExist(key Key) bool { - if !slf.atom { - slf.lock.Lock() - defer slf.lock.Unlock() - } - if _, exist := slf.data[key]; !exist { - return exist - } - delete(slf.data, key) - return true -} - -func (slf *Map[Key, Value]) Clear() { - if !slf.atom { - slf.lock.Lock() - defer slf.lock.Unlock() - } - for k := range slf.data { - delete(slf.data, k) - } -} - -func (slf *Map[Key, Value]) ClearHandle(handle func(key Key, value Value)) { - if !slf.atom { - slf.lock.Lock() - defer slf.lock.Unlock() - } - for k, v := range slf.data { - handle(k, v) - delete(slf.data, k) - } -} - -func (slf *Map[Key, Value]) Range(handle func(key Key, value Value)) { - if !slf.atom { - slf.lock.RLock() - defer slf.lock.RUnlock() - } - for k, v := range slf.data { - key, value := k, v - handle(key, value) - } -} - -func (slf *Map[Key, Value]) RangeSkip(handle func(key Key, value Value) bool) { - if !slf.atom { - slf.lock.RLock() - defer slf.lock.RUnlock() - } - for k, v := range slf.data { - key, value := k, v - if !handle(key, value) { - continue - } - } -} - -func (slf *Map[Key, Value]) RangeBreakout(handle func(key Key, value Value) bool) { - slf.rangeBreakout(handle) -} - -func (slf *Map[Key, Value]) rangeBreakout(handle func(key Key, value Value) bool) bool { - if !slf.atom { - slf.lock.RLock() - defer slf.lock.RUnlock() - } - for k, v := range slf.data { - key, value := k, v - if !handle(key, value) { - return true - } - } - return false -} - -func (slf *Map[Key, Value]) RangeFree(handle func(key Key, value Value, skip func(), breakout func())) { - slf.rangeFree(handle) -} - -func (slf *Map[Key, Value]) rangeFree(handle func(key Key, value Value, skip func(), breakout func())) bool { - var skipExec, breakoutExec bool - var skip = func() { - skipExec = true - } - var breakout = func() { - breakoutExec = true - } - if !slf.atom { - slf.lock.RLock() - defer slf.lock.RUnlock() - } - for k, v := range slf.data { - key, value := k, v - handle(key, value, skip, breakout) - if skipExec { - continue - } - if breakoutExec { - break - } - } - return breakoutExec -} - -func (slf *Map[Key, Value]) Keys() []Key { - if !slf.atom { - slf.lock.RLock() - defer slf.lock.RUnlock() - } - var s = make([]Key, 0, len(slf.data)) - for k := range slf.data { - s = append(s, k) - } - return s -} - -func (slf *Map[Key, Value]) Slice() []Value { - if !slf.atom { - slf.lock.RLock() - defer slf.lock.RUnlock() - } - var s = make([]Value, 0, len(slf.data)) - for _, v := range slf.data { - s = append(s, v) - } - return s -} - -func (slf *Map[Key, Value]) Map() map[Key]Value { - var m = make(map[Key]Value) - if !slf.atom { - slf.lock.RLock() - defer slf.lock.RUnlock() - } - for k, v := range slf.data { - m[k] = v - } - return m -} - -func (slf *Map[Key, Value]) Size() int { - if !slf.atom { - slf.lock.RLock() - defer slf.lock.RUnlock() - } - return len(slf.data) -} - -// GetOne 获取一个 -func (slf *Map[Key, Value]) GetOne() (value Value) { - if !slf.atom { - slf.lock.RLock() - defer slf.lock.RUnlock() - } - for _, v := range slf.data { - return v - } - return value -} - -func (slf *Map[Key, Value]) MarshalJSON() ([]byte, error) { - m := slf.Map() - return json.Marshal(m) -} - -func (slf *Map[Key, Value]) UnmarshalJSON(bytes []byte) error { - var m = make(map[Key]Value) - if err := json.Unmarshal(bytes, &m); err != nil { - return err - } - if !slf.atom { - slf.lock.Lock() - defer slf.lock.Unlock() - } - slf.data = m - return nil -} diff --git a/utils/synchronization/map_options.go b/utils/synchronization/map_options.go deleted file mode 100644 index 0286200..0000000 --- a/utils/synchronization/map_options.go +++ /dev/null @@ -1,10 +0,0 @@ -package synchronization - -type MapOption[Key comparable, Value any] func(m *Map[Key, Value]) - -// WithMapSource 通过传入的 map 初始化 -func WithMapSource[Key comparable, Value any](source map[Key]Value) MapOption[Key, Value] { - return func(m *Map[Key, Value]) { - m.data = source - } -} \ No newline at end of file diff --git a/utils/synchronization/map_segment.go b/utils/synchronization/map_segment.go deleted file mode 100644 index 414e72e..0000000 --- a/utils/synchronization/map_segment.go +++ /dev/null @@ -1,259 +0,0 @@ -package synchronization - -import ( - "encoding/json" - "github.com/kercylan98/minotaur/utils/hash" - "sync" -) - -func NewMapSegment[Key comparable, value any](segmentCount int) *MapSegment[Key, value] { - ms := &MapSegment[Key, value]{ - segments: map[int]*Map[Key, value]{}, - cache: map[Key]int{}, - consistency: hash.NewConsistency(segmentCount), - } - for i := 0; i < segmentCount; i++ { - ms.consistency.AddNode(i) - ms.segments[i] = NewMap[Key, value]() - } - - return ms -} - -// MapSegment 基于分段锁实现的并发安全的字典数据结构map -type MapSegment[Key comparable, Value any] struct { - segments map[int]*Map[Key, Value] - cache map[Key]int - consistency *hash.Consistency - lock sync.RWMutex -} - -func (slf *MapSegment[Key, Value]) Atom(handle func(m hash.Map[Key, Value])) { - panic("this function is currently not supported") -} - -func (slf *MapSegment[Key, Value]) Set(key Key, value Value) { - slf.lock.RLock() - s, exist := slf.cache[key] - slf.lock.RUnlock() - if !exist { - s = slf.consistency.PickNode(key) - slf.lock.Lock() - slf.cache[key] = s - slf.lock.Unlock() - } - slf.segments[s].Set(key, value) -} - -func (slf *MapSegment[Key, Value]) Get(key Key) (value Value) { - slf.lock.RLock() - s, exist := slf.cache[key] - slf.lock.RUnlock() - if !exist { - return value - } - return slf.segments[s].Get(key) -} - -// AtomGetSet 原子方式获取一个值并在之后进行赋值 -func (slf *MapSegment[Key, Value]) AtomGetSet(key Key, handle func(value Value, exist bool) (newValue Value, isSet bool)) { - var value Value - slf.lock.RLock() - s, exist := slf.cache[key] - slf.lock.RUnlock() - if !exist { - if newValue, isSet := handle(value, exist); isSet { - slf.Set(key, newValue) - } - return - } - slf.segments[s].AtomGetSet(key, handle) -} - -func (slf *MapSegment[Key, Value]) Exist(key Key) bool { - slf.lock.RLock() - _, exist := slf.cache[key] - slf.lock.RUnlock() - return exist -} - -func (slf *MapSegment[Key, Value]) GetExist(key Key) (value Value, exist bool) { - slf.lock.RLock() - s, exist := slf.cache[key] - slf.lock.RUnlock() - if !exist { - return value, false - } - return slf.segments[s].GetExist(key) -} - -func (slf *MapSegment[Key, Value]) Delete(key Key) { - slf.lock.Lock() - s, exist := slf.cache[key] - delete(slf.cache, key) - slf.lock.Unlock() - if exist { - slf.segments[s].Delete(key) - } -} - -func (slf *MapSegment[Key, Value]) DeleteGet(key Key) (value Value) { - slf.lock.Lock() - s, exist := slf.cache[key] - delete(slf.cache, key) - slf.lock.Unlock() - if exist { - return slf.segments[s].DeleteGet(key) - } - return -} - -func (slf *MapSegment[Key, Value]) DeleteGetExist(key Key) (value Value, exist bool) { - slf.lock.Lock() - s, exist := slf.cache[key] - delete(slf.cache, key) - slf.lock.Unlock() - if exist { - return slf.segments[s].DeleteGetExist(key) - } - return value, exist -} - -func (slf *MapSegment[Key, Value]) DeleteExist(key Key) bool { - slf.lock.Lock() - s, exist := slf.cache[key] - delete(slf.cache, key) - slf.lock.Unlock() - if exist { - return slf.segments[s].DeleteExist(key) - } - return false -} - -func (slf *MapSegment[Key, Value]) Clear() { - slf.lock.Lock() - for k := range slf.cache { - delete(slf.cache, k) - } - for _, m := range slf.segments { - m.Clear() - } - slf.lock.Unlock() -} - -func (slf *MapSegment[Key, Value]) ClearHandle(handle func(key Key, value Value)) { - slf.lock.Lock() - for k := range slf.cache { - delete(slf.cache, k) - } - for _, m := range slf.segments { - m.ClearHandle(handle) - } - slf.lock.Unlock() -} - -func (slf *MapSegment[Key, Value]) Range(handle func(key Key, value Value)) { - for _, m := range slf.segments { - m.Range(handle) - } -} - -func (slf *MapSegment[Key, Value]) RangeSkip(handle func(key Key, value Value) bool) { - for _, m := range slf.segments { - m.RangeSkip(handle) - } -} - -func (slf *MapSegment[Key, Value]) RangeBreakout(handle func(key Key, value Value) bool) { - for _, m := range slf.segments { - if m.rangeBreakout(handle) { - break - } - } -} - -func (slf *MapSegment[Key, Value]) RangeFree(handle func(key Key, value Value, skip func(), breakout func())) { - for _, m := range slf.segments { - if m.rangeFree(handle) { - break - } - } -} - -func (slf *MapSegment[Key, Value]) Keys() []Key { - var s = make([]Key, 0, len(slf.cache)) - slf.lock.RLock() - for k := range slf.cache { - s = append(s, k) - } - defer slf.lock.RUnlock() - return s -} - -func (slf *MapSegment[Key, Value]) Slice() []Value { - slf.lock.RLock() - var s = make([]Value, 0, len(slf.cache)) - slf.lock.RUnlock() - for _, m := range slf.segments { - s = append(s, m.Slice()...) - } - return s -} - -func (slf *MapSegment[Key, Value]) Map() map[Key]Value { - slf.lock.RLock() - var s = map[Key]Value{} - slf.lock.RUnlock() - for _, m := range slf.segments { - for k, v := range m.Map() { - s[k] = v - } - } - return s -} - -func (slf *MapSegment[Key, Value]) Size() int { - slf.lock.RLock() - defer slf.lock.RUnlock() - return len(slf.cache) -} - -// GetOne 获取一个 -func (slf *MapSegment[Key, Value]) GetOne() (value Value) { - for k, s := range slf.cache { - return slf.segments[s].Get(k) - } - return value -} - -func (slf *MapSegment[Key, Value]) MarshalJSON() ([]byte, error) { - var ms struct { - Segments map[int]*Map[Key, Value] - Cache map[Key]int - SegmentCount int - } - ms.Segments = slf.segments - ms.Cache = slf.cache - ms.SegmentCount = len(slf.segments) - return json.Marshal(ms) -} - -func (slf *MapSegment[Key, Value]) UnmarshalJSON(bytes []byte) error { - var ms struct { - Segments map[int]*Map[Key, Value] - Cache map[Key]int - SegmentCount int - } - if err := json.Unmarshal(bytes, &ms); err != nil { - return err - } - slf.lock.Lock() - slf.consistency = hash.NewConsistency(ms.SegmentCount) - for i := 0; i < ms.SegmentCount; i++ { - slf.consistency.AddNode(i) - } - slf.segments = ms.Segments - slf.cache = ms.Cache - slf.lock.Unlock() - return nil -}