GO語(yǔ)言處理多個(gè)布爾選項(xiàng)的實(shí)現(xiàn)過(guò)程
一、需求背景
在 go 語(yǔ)言開(kāi)發(fā)中,經(jīng)常會(huì)遇到類(lèi)似下面需要定義多個(gè)布爾類(lèi)型參數(shù)的場(chǎng)景:
- 日志打印方式配置參數(shù),包含是否打印文件名、是否打印事件、是否打印函數(shù)名等
- 打開(kāi)文件的方式參數(shù),包含是否只讀、是否只寫(xiě)、是否創(chuàng)建、是否追加等
實(shí)現(xiàn)上述需求,常規(guī)的做法是定義多個(gè)布爾類(lèi)型的參數(shù),如:
// 文件操作配置類(lèi)
type Config struct {
IsReadOnly bool
IsWriteOnly bool
IsCreate bool
// ... 每個(gè)標(biāo)志都需要一個(gè)字段
}上述方案的缺點(diǎn):
- 占用內(nèi)存多
- 不便于傳參
二、實(shí)現(xiàn)方案
2.1 方案思路
在 go 語(yǔ)言中,一種更高效的實(shí)現(xiàn)方案是通過(guò)定義常量并使用位運(yùn)算組合多個(gè)布爾選項(xiàng),每個(gè)選項(xiàng)對(duì)應(yīng)一個(gè)二進(jìn)制位(bit),即一個(gè)布爾標(biāo)志
// 文件操作類(lèi)型常量,按 2 的冪次賦值,每個(gè)類(lèi)型占用一個(gè)二進(jìn)制中的位
const (
O_RDONLY = 1 << iota // 1 (0b0001)
O_WRONLY // 2 (0b0010)
O_CREAT // 4 (0b0100)
)
// 文件操作配置類(lèi)
type Config struct {
Flags int // 文件操作類(lèi)型標(biāo)識(shí)位
}
// 設(shè)置多個(gè)標(biāo)志
config.Flags = O_CREAT | O_WRONLY
// 清除標(biāo)識(shí)
config.Flags &^= O_CREAT // 清除 O_CREAT 標(biāo)志
func Process(flags int) {
// 檢查多個(gè)標(biāo)志
if flags & (O_CREAT | O_WRONLY) != 0 {
// 處理邏輯...
}
// ...
}2.2 工作原理
假設(shè) Flags = 3(二進(jìn)制 00000011),則 Flags & O_WRONLY 的結(jié)果如下:
00000011 (Flags = 3)
& 00000010 (O_WRONLY = 2)
-------------------------------------
00000010 (結(jié)果 = 2)
結(jié)果不為 0, 即 Flags & O_WRONLY != 0,說(shuō)明標(biāo)識(shí)位中含有 O_WRONLY
2.3 方案優(yōu)點(diǎn)
①高效存儲(chǔ):使用一個(gè)整數(shù)(如uint8, uint16, uint32等)的各個(gè)二進(jìn)制位來(lái)存儲(chǔ)多個(gè)布爾標(biāo)志,可以節(jié)省內(nèi)存。例如,一個(gè)32位整數(shù)可以存儲(chǔ)32個(gè)不同的標(biāo)志,而如果每個(gè)標(biāo)志都用一個(gè)bool變量(通常占1個(gè)字節(jié))存儲(chǔ),則需要32字節(jié)。
②快速組合和檢查:可以同時(shí)設(shè)置和檢查多個(gè)標(biāo)志。
// 同時(shí)設(shè)置多個(gè)標(biāo)志 config.Flags = O_CREAT | O_WRONLY
③性能高:位運(yùn)算CPU原生支持的操作,因此檢查標(biāo)志位的速度很快,比哈希表查找、字符串比較等快得多。
④便于參數(shù)傳遞:由于所有標(biāo)志都存儲(chǔ)在一個(gè)整數(shù)中,因此可以方便地作為函數(shù)參數(shù)傳遞、存儲(chǔ)在結(jié)構(gòu)體中或序列化到文件中。
三、實(shí)際案例
3.1 GO 源碼中文件操作配置的相關(guān)源碼
// 源碼位置:src/os/file.go
// Flags to OpenFile wrapping those of the underlying system. Not all
// flags may be implemented on a given system.
const (
// Exactly one of O_RDONLY, O_WRONLY, or O_RDWR must be specified.
O_RDONLY int = syscall.O_RDONLY // open the file read-only.
O_WRONLY int = syscall.O_WRONLY // open the file write-only.
O_RDWR int = syscall.O_RDWR // open the file read-write.
// The remaining values may be or'ed in to control behavior.
O_APPEND int = syscall.O_APPEND // append data to the file when writing.
O_CREATE int = syscall.O_CREAT // create a new file if none exists.
O_EXCL int = syscall.O_EXCL // used with O_CREATE, file must not exist.
O_SYNC int = syscall.O_SYNC // open for synchronous I/O.
O_TRUNC int = syscall.O_TRUNC // truncate regular writable file when opened.
)
const (
// Invented values to support what package os expects.
O_RDONLY = 0x00000
O_WRONLY = 0x00001
O_RDWR = 0x00002
O_CREAT = 0x00040
O_EXCL = 0x00080
O_NOCTTY = 0x00100
O_TRUNC = 0x00200
O_NONBLOCK = 0x00800
O_APPEND = 0x00400
O_SYNC = 0x01000
O_ASYNC = 0x02000
O_CLOEXEC = 0x80000
o_DIRECTORY = 0x100000 // used by internal/syscall/windows
o_NOFOLLOW_ANY = 0x20000000 // used by internal/syscall/windows
o_OPEN_REPARSE = 0x40000000 // used by internal/syscall/windows
)
// 源碼位置:src/syscall/syscall_windows.go
func Open(name string, flag int, perm uint32) (fd Handle, err error) {
if len(name) == 0 {
return InvalidHandle, ERROR_FILE_NOT_FOUND
}
namep, err := UTF16PtrFromString(name)
if err != nil {
return InvalidHandle, err
}
var access uint32
switch flag & (O_RDONLY | O_WRONLY | O_RDWR) {
case O_RDONLY:
access = GENERIC_READ
case O_WRONLY:
access = GENERIC_WRITE
case O_RDWR:
access = GENERIC_READ | GENERIC_WRITE
}
if flag&O_CREAT != 0 {
access |= GENERIC_WRITE
}
// ...
return h, nil
}3.2 goframe 框架中記錄日志內(nèi)容的配置項(xiàng)相關(guān)源碼
// 源碼位置:pkg/mod/github.com/gogf/gf@v1.16.9/os/glog/glog_logger.go
// 設(shè)置日志打印選項(xiàng)常量值
const (
F_ASYNC = 1 << iota // Print logging content asynchronously。
F_FILE_LONG // Print full file name and line number: /a/b/c/d.go:23.
F_FILE_SHORT // Print final file name element and line number: d.go:23. overrides F_FILE_LONG.
F_TIME_DATE // Print the date in the local time zone: 2009-01-23.
F_TIME_TIME // Print the time in the local time zone: 01:23:23.
F_TIME_MILLI // Print the time with milliseconds in the local time zone: 01:23:23.675.
F_CALLER_FN // Print Caller function name and package: main.main
F_TIME_STD = F_TIME_DATE | F_TIME_MILLI
)
// 源碼位置:pkg/mod/github.com/gogf/gf@v1.16.9/os/glog/glog_logger_config.go
// Config is the configuration object for logger.
type Config struct {
Handlers []Handler `json:"-"` // Logger handlers which implement feature similar as middleware.
Writer io.Writer `json:"-"` // Customized io.Writer.
// 日志打印選項(xiàng)標(biāo)識(shí)位
Flags int `json:"flags"` // Extra flags for logging output features.
// ...
}
func DefaultConfig() Config {
c := Config{
File: defaultFileFormat,
Flags: F_TIME_STD, // 打印內(nèi)容默認(rèn)設(shè)置為 標(biāo)準(zhǔn)時(shí)間格式
Level: LEVEL_ALL,
CtxKeys: []interface{}{gctx.CtxKey},
// ...
}
// ...
return c
}
// SetAsync enables/disables async logging output feature.
func (l *Logger) SetAsync(enabled bool) {
if enabled {
l.config.Flags = l.config.Flags | F_ASYNC // 設(shè)置 F_ASYNC 標(biāo)識(shí)位
} else {
l.config.Flags = l.config.Flags & ^F_ASYNC // 清除 F_ASYNC 標(biāo)識(shí)位
}
}
// 源碼位置:pkg/mod/github.com/gogf/gf@v1.16.9/os/glog/glog_logger.go
// print prints `s` to defined writer, logging file or passed `std`.
func (l *Logger) print(ctx context.Context, level int, values ...interface{}) {
// ...
if l.config.HeaderPrint {
// Time.
timeFormat := ""
if l.config.Flags&F_TIME_DATE > 0 { // 判斷日志打印選項(xiàng)標(biāo)識(shí)位中是否含有 F_TIME_DATE
timeFormat += "2006-01-02"
}
if l.config.Flags&F_TIME_TIME > 0 {
if timeFormat != "" {
timeFormat += " "
}
timeFormat += "15:04:05"
}
if l.config.Flags&F_TIME_MILLI > 0 {
if timeFormat != "" {
timeFormat += " "
}
timeFormat += "15:04:05.000"
}
// ...
}
// ...
}3.3 Go 標(biāo)準(zhǔn)庫(kù)中的互斥鎖相關(guān)源碼
// 位于 src/sync/mutex.go
type Mutex struct {
state int32 // 鎖狀態(tài)和一些標(biāo)志位
sema uint32 // 信號(hào)量,用于阻塞和喚醒等待的 goroutine
}
const (
mutexLocked = 1 << iota // 是否被鎖定
mutexWoken // 是否有協(xié)程被喚醒
mutexStarving // 是否處于饑餓模式
mutexWaiterShift = iota // 表示等待鎖的阻塞協(xié)程個(gè)數(shù)
starvationThresholdNs = 1e6
)總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
GoLang BoltDB數(shù)據(jù)庫(kù)詳解
這篇文章主要介紹了GoLang BoltDB數(shù)據(jù)庫(kù),boltdb是使用Go語(yǔ)言編寫(xiě)的開(kāi)源的鍵值對(duì)數(shù)據(jù)庫(kù),boltdb存儲(chǔ)數(shù)據(jù)時(shí) key和value都要求是字節(jié)數(shù)據(jù),此處需要使用到 序列化和反序列化2023-02-02
golang使用RSA加密和解密的實(shí)現(xiàn)示例
在Golang中RSA加密和解密是一個(gè)常見(jiàn)的操作,本文主要介紹了golang使用RSA加密和解密的實(shí)現(xiàn)示例,具有一定的參考價(jià)值,感興趣的可以了解一下2025-03-03
Go語(yǔ)言函數(shù)的延遲調(diào)用(Deferred Code)詳解
本文將介紹Go語(yǔ)言函數(shù)和方法中的延遲調(diào)用,正如名稱一樣,這部分定義不會(huì)立即執(zhí)行,一般會(huì)在函數(shù)返回前再被調(diào)用,我們通過(guò)一些示例來(lái)了解一下延遲調(diào)用的使用場(chǎng)景2022-07-07
Gin golang web開(kāi)發(fā)模型綁定實(shí)現(xiàn)過(guò)程解析
這篇文章主要介紹了Gin golang web開(kāi)發(fā)模型綁定實(shí)現(xiàn)過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-10-10

