GO语言处理多个布尔选项的实现过程

 更新时间:2026年06月23日 08:59:33   作者:龙门吹雪  
这段文章详细介绍了在Go语言中使用位运算和Flags常量高效管理多个布尔配置参数的方法,通过位运算实现高效存储、组合与检查,相比传统方法节省内存并提升性能若您正在处理类似需求,此方案值得尝试

一、需求背景

在 go 语言开发中,经常会遇到类似下面需要定义多个布尔类型参数的场景:

  • 日志打印方式配置参数,包含是否打印文件名、是否打印事件、是否打印函数名等
  • 打开文件的方式参数,包含是否只读、是否只写、是否创建、是否追加等

实现上述需求,常规的做法是定义多个布尔类型的参数,如:

// 文件操作配置类
type Config struct {
    IsReadOnly   bool
    IsWriteOnly  bool
    IsCreate     bool
    // ... 每个标志都需要一个字段
}

上述方案的缺点:

  • 占用内存多
  • 不便于传参

二、实现方案

2.1 方案思路

在 go 语言中,一种更高效的实现方案是通过定义常量并使用位运算组合多个布尔选项,每个选项对应一个二进制位(bit),即一个布尔标志

// 文件操作类型常量,按 2 的幂次赋值,每个类型占用一个二进制中的位
const (
    O_RDONLY = 1 << iota // 1 (0b0001)
    O_WRONLY             // 2 (0b0010)
    O_CREAT              // 4 (0b0100)
)

// 文件操作配置类
type Config struct {
    Flags int // 文件操作类型标识位
}

// 设置多个标志
config.Flags = O_CREAT | O_WRONLY

// 清除标识
config.Flags &^= O_CREAT  // 清除 O_CREAT 标志

func Process(flags int) {
    // 检查多个标志
    if flags & (O_CREAT | O_WRONLY) != 0 {
        // 处理逻辑...
    }

    // ...    
}

2.2 工作原理

假设 Flags = 3(二进制 00000011),则 Flags & O_WRONLY 的结果如下:

   00000011 (Flags = 3)

& 00000010 (O_WRONLY = 2)

    -------------------------------------

    00000010 (结果 = 2)

结果不为 0, 即 Flags & O_WRONLY  != 0,说明标识位中含有 O_WRONLY

2.3 方案优点

①高效存储:使用一个整数(如uint8, uint16, uint32等)的各个二进制位来存储多个布尔标志,可以节省内存。例如,一个32位整数可以存储32个不同的标志,而如果每个标志都用一个bool变量(通常占1个字节)存储,则需要32字节。

②快速组合和检查:可以同时设置和检查多个标志。

// 同时设置多个标志
config.Flags = O_CREAT | O_WRONLY

③性能高:位运算CPU原生支持的操作,因此检查标志位的速度很快,比哈希表查找、字符串比较等快得多。

④便于参数传递:由于所有标志都存储在一个整数中,因此可以方便地作为函数参数传递、存储在结构体中或序列化到文件中。

三、实际案例

3.1 GO 源码中文件操作配置的相关源码

// 源码位置: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 框架中记录日志内容的配置项相关源码

// 源码位置:pkg/mod/github.com/gogf/gf@v1.16.9/os/glog/glog_logger.go

// 设置日志打印选项常量值
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.
    // 日志打印选项标识位
	Flags                int            `json:"flags"`                // Extra flags for logging output features.
    // ...
}

func DefaultConfig() Config {
	c := Config{
		File:                defaultFileFormat,
		Flags:               F_TIME_STD,  // 打印内容默认设置为 标准时间格式
		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   // 设置 F_ASYNC 标识位
	} else {
		l.config.Flags = l.config.Flags & ^F_ASYNC  // 清除 F_ASYNC 标识位
	}
}

// 源码位置: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 {  // 判断日志打印选项标识位中是否含有 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 标准库中的互斥锁相关源码

// 位于 src/sync/mutex.go
 
type Mutex struct {
	state int32     // 锁状态和一些标志位
	sema  uint32    // 信号量,用于阻塞和唤醒等待的 goroutine
}
 
const (
	mutexLocked = 1 << iota // 是否被锁定  
	mutexWoken              // 是否有协程被唤醒
	mutexStarving           // 是否处于饥饿模式
	mutexWaiterShift = iota // 表示等待锁的阻塞协程个数
	starvationThresholdNs = 1e6
)

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • golang trace view视图详解

    golang trace view视图详解

    在golang中可以使用go pprof的工具对golang程序进行性能分析,其中通过go trace 命令生成的trace view视图对于我们分析系统延迟十分有帮助,鉴于当前对trace view视图的介绍还是很少,在粗略的看过trace统计原理后,将对这部分做比较详细的介绍
    2023-08-08
  • 详解go如何优雅的使用接口与继承

    详解go如何优雅的使用接口与继承

    Go语言中的接口和嵌套结构体是两种重要的代码设计方式,接口定义了一组方法签名,使得不同的类型能够以相同的方式进行交互,本文将给大家介绍go语言如何优雅的使用接口与继承,文中有详细的代码供大家参考,需要的朋友可以参考下
    2024-06-06
  • Golang http请求封装的代码示例

    Golang http请求封装的代码示例

    http请求封装在项目中非常普遍,下面笔者封装了http post请求传json、form 和get请求,以备将来使用,文中代码示例介绍的非常详细,需要的朋友可以参考下
    2023-06-06
  • Golang中Retry重试实践

    Golang中Retry重试实践

    本文主要介绍了Golang中Retry重试实践,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2026-02-02
  • GoLang BoltDB数据库详解

    GoLang BoltDB数据库详解

    这篇文章主要介绍了GoLang BoltDB数据库,boltdb是使用Go语言编写的开源的键值对数据库,boltdb存储数据时 key和value都要求是字节数据,此处需要使用到 序列化和反序列化
    2023-02-02
  • go中的protobuf和grpc使用教程

    go中的protobuf和grpc使用教程

    gRPC 是 Google 公司基于 Protobuf 开发的跨语言的开源 RPC 框架,这篇文章主要介绍了go中的protobuf和grpc使用教程,需要的朋友可以参考下
    2024-08-08
  • golang使用RSA加密和解密的实现示例

    golang使用RSA加密和解密的实现示例

    在Golang中RSA加密和解密是一个常见的操作,本文主要介绍了golang使用RSA加密和解密的实现示例,具有一定的参考价值,感兴趣的可以了解一下
    2025-03-03
  • 如何在golang中检查文件是否存在

    如何在golang中检查文件是否存在

    如果你用的是 Python,可通过 os.path.exists 这样的标准库函数实现,遗憾的是,Go 标准库没有提供这样直接的函数,所以下面我们就来了解下如何使用GO语言能实现检查文件是否存在呢
    2024-02-02
  • Go语言函数的延迟调用(Deferred Code)详解

    Go语言函数的延迟调用(Deferred Code)详解

    本文将介绍Go语言函数和方法中的延迟调用,正如名称一样,这部分定义不会立即执行,一般会在函数返回前再被调用,我们通过一些示例来了解一下延迟调用的使用场景
    2022-07-07
  • Gin golang web开发模型绑定实现过程解析

    Gin golang web开发模型绑定实现过程解析

    这篇文章主要介绍了Gin golang web开发模型绑定实现过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-10-10

最新评论