【Golang】defer的使用和底层实现

深入理解Go语言defer:资源管理、异常捕获与底层机制,

📚 全文字数 : 3k

⏳ 阅读时长 : 5min

📢 关键词 : defer、资源释放、底层实现

1:defer是什么

defer是Go语言的关键字,一般用于资源的释放和异常的捕捉(比如:文件打开、加锁、数据库连接、异常捕获),defer语句后将其后面跟随的语句进行延迟处理,就是说在函数执行完毕后再执行调用,也就是return的ret指令之前。

1.1 资源释放

资源的释放在代码中有很多场景,比如打开文件描述符资源后,需要进行file.close得到释放,在打开文件后就加上defer,避免在后续因为err导致的return退出忘记释放,文件资源。

func openFile() {
	file, err := os.Open("txt")
	if err != nil {
		return
	}
	defer file.Close() //合理位置
}

常见的加锁场景,业务代码中忘记释放锁,那么会导致资源得不到释放,造成死锁,但是defer就很好解决了这个问题,不管业务逻辑怎么处理,最终还是会释放锁。

func lockScene() {
	 var mutex sync.Mutex
	 mutex.Lock()
	 defer  mutex.Unlock()
	 //业务代码...
}

1.2 捕获异常

Go 语言中 recover 关键字主要用于捕获异常,让程序回到正常状态。recover 可以中止 panic 造成的程序崩溃。它是一个只能在 defer 中发挥作用的函数,在其他作用域中调用不会发挥作用

func demo()  {
 defer func() {
  if err := recover(); err !=nil{
   fmt.Println(string(Stack
### Golang 实现任务队列 在 Go 语言中,可以通过多种方式实现任务队列。以下是几种常见的方法及其对应的示例代码。 #### 方法一:基于切片的简单队列 通过定义一个结构体并使用切片作为底层存储机制,可以轻松实现一个基本的任务队列。此方法适合小型项目或学习目的。 ```go package main import "fmt" type Queue struct { items []interface{} } func (q *Queue) Enqueue(item interface{}) { q.items = append(q.items, item) } func (q *Queue) Dequeue() interface{} { if len(q.items) == 0 { return nil } item := q.items[0] q.items = q.items[1:] return item } func (q *Queue) Length() int { return len(q.items) } func main() { queue := &Queue{} fmt.Println(queue.Length()) // 输出: 0 queue.Enqueue("Task 1") queue.Enqueue("Task 2") fmt.Println(queue.Dequeue()) // 输出: Task 1 fmt.Println(queue.Length()) // 输出: 1 } ``` 这种方法适用于简单的场景[^1]。 --- #### 方法二:基于通道(Channel)的任务队列 Go 的并发特性使其非常适合用于构建高效的任务队列。利用 `channel` 多个 Goroutines 可以创建一个高效的生产者-消费者模式。 ```go package main import ( "fmt" "time" ) func worker(id int, jobs <-chan string, results chan<- string) { for job := range jobs { fmt.Printf("Worker %d processing task '%s'\n", id, job) time.Sleep(time.Second) // Simulate work being done results <- fmt.Sprintf("Result of %s by Worker %d", job, id) } } func main() { const numJobs = 5 jobs := make(chan string, numJobs) results := make(chan string, numJobs) // Start workers for w := 1; w <= 3; w++ { go worker(w, jobs, results) } // Send tasks to the queue for j := 1; j <= numJobs; j++ { jobs <- fmt.Sprintf("Job-%d", j) } close(jobs) // Collect all results for a := 1; a <= numJobs; a++ { result := <-results fmt.Println(result) } } ``` 上述代码展示了如何使用通道 Goroutines 来分发任务,并让多个工作线程处理它们。这种方式能够有效避免单个消费者的任务积压问题[^2]。 --- #### 方法三:集成 RabbitMQ 或其他消息中间件 对于更复杂的分布式系统,建议使用专业的消息队列服务(如 RabbitMQ)。为了确保任务公平分配给所有可用消费者,可以在客户端启用 QoS 设置: ```go package main import ( "log" "github.com/streadway/amqp" ) func failOnError(err error, msg string) { if err != nil { log.Fatalf("%s: %s", msg, err) } } func main() { conn, err := amqp.Dial("amqp://guest:guest@localhost/") failOnError(err, "Failed to connect to RabbitMQ") defer conn.Close() ch, err := conn.Channel() failOnError(err, "Failed to open a channel") defer ch.Close() err = ch.Qos( 1, // prefetch count 0, // prefetch size false, // global ) failOnError(err, "Failed to set qos") msgs, err := ch.Consume( "task_queue", // queue "", // consumer false, // auto-ack false, // exclusive false, // no-local false, // no-wait nil, // args ) failOnError(err, "Failed to register a consumer") forever := make(chan bool) go func() { for d := range msgs { log.Printf("Received a message: %s", d.Body) d.Ack(false) } }() log.Printf(" [*] Waiting for messages. To exit press CTRL+C") <-forever } ``` 这段代码设置了每名消费者最多只能同时处理一条未完成的任务,从而防止某些消费者过载而另一些闲置的情况发生[^3]。 --- #### 方法四:环形队列的应用扩展 如果需要进一步优化性能或者支持特定功能(例如流量控制),则可采用环形队列的设计思路。这种方案特别适合于高吞吐量的数据流管理。 ```go package main import "sync/atomic" const capacity = 8 type CircularQueue struct { data [capacity]interface{} head uint64 tail uint64 size uint64 capcity uint64 } func NewCircularQueue(capacity uint64) *CircularQueue { return &CircularQueue{capcity: capacity} } func (cq *CircularQueue) Push(value interface{}) bool { if cq.size >= cq.capcity { return false } index := atomic.AddUint64(&cq.tail, 1)%cq.capcity cq.data[index] = value atomic.AddUint64(&cq.size, 1) return true } func (cq *CircularQueue) Pop() interface{} { if cq.IsEmpty() { return nil } index := atomic.AddUint64(&cq.head, 1)%cq.capcity value := cq.data[index] atomic.AddUint64(&cq.size, ^uint64(0)) // Decrement size using two's complement. return value } func (cq *CircularQueue) IsEmpty() bool { return atomic.LoadUint64(&cq.size) == 0 } ``` 该实现解决了传统队列可能遇到的“假溢出”问题,提高了资源利用率[^5]。 --- #### 总结 以上介绍了四种不同的任务队列实现方式,分别针对不同需求提供了灵活的选择。无论是基础版还是高级版本,都可以根据实际应用场景调整参数配置。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值