Go语言资源自动回收技术

前端之家收集整理的这篇文章主要介绍了Go语言资源自动回收技术前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

Go语言作为一个现代化的编程语言以及支持垃圾内存的自动回收特性(GC). 我们现在关注的是非内存资源的自动回收技术.

局部资源的管理

在讨论Go语言解决方案之前,我们先看看C++是怎么管理资源的.

C++中可以可以自动执行的代码主要是构造函数和析构函数. 因此,很多资源的管理技术都是基于构造函数和析构函数实现.

比较常见的是C++的RAII(Resource Acquisition Is Initialization)技术,即初始化中获取资源. 比如在多线程编程中用到的MutexLocker:

struct MutexLock {
	Mutex *const mu_;
	MutexLock(Mutex *mu): mu_(mu)  {
		mu_->Lock();
	}
	~MutexLock() {
		mu_->Unlock();
	}
};

这样在使用Mutex的时候就不会忘记解锁的操作了:

void* safeRead(Mutex *mu) {
	MutexLock locker(mu);
	if(...) {
		return NULL;
	}
	return read();
}

其实RAII中最重要的是退出locker作用域是自动执行对象的析构函数,这里也就是mu_->Unlock();语句.

C++的构造函数其实是次要的. 关于禁用C++构造函数的讨论可以参考我的 另一个文章: C++去掉构造函数会怎么样?

因为构造函数经常是通过显示定义变量而隐式调用的,因此用普通的全局函数也 可以实现构造函数功能(唯一的约束是值容器). 其实C语言的fopen就是一个FILE对象的构造函数.

而作为C语言简约哲学继承者的Go语言同样也没有对构造函数做特殊处理. 在Go语言中构造函数这是约定以New开头的普通函数,比如NewBuffer.

Go语言/UNIX之父Ken Thompson发明了defer语句,完美地 解决了析构函数的问题(defer还有很多其他特性).

因此,在释放局部资源时,可以用defer管理. 因为C++的RAII的构造 函数和析构函数耦合过于紧密,对于资源申请失败的问题就比较麻烦. 但是Go语言的defer则灵活很多.

比如,Go语言版本基于deferMutex用法

func safeRead(Mutex *mu) []byte {
	mu.Lock()
	defer mu.Unlock()
	return read();
}

对于可能申请失败的资源也很好处理:

func loadFile(name string) ([]byte,error) {
	f,err := os.Open(name)
	if err != nil {
		return nil,err
	}
	defer f.Close()
	return load(f)
}

使用defer语句,可以方便地组合函数/闭包和资源对象. 即使panic时,defer也能保证资源的正确释放.

非局部资源的管理

我们之前看到的都是在局部使用和释放资源. 如果资源的生命周期很长,而且可能被多个模块共享和随意传递的话,defer语句就不好处理了.

解决的思路和C++的RAII的方式类似: 我们需要一个能够自己定义的类似 析构函数的技术.

但是因为Go语言有GC特性,因此没有析构函数的概念. 不过runtime包的 func SetFinalizer(x,f interface{})函数可以提供类似的机制.

比如,我们可以包装一个文件对象,在没有人使用的时候能够自动关闭:

type MyFile struct {
	f *os.File
}

func NewFile(name string) (*MyFile,err
	}
	runtime.SetFinalizer(f,f.Close)
	return &MyFile{f:f},nil
}
func (f *MyFile) Close() {
	f.f.Close()
}

在使用runtime.SetFinalizer时,需要注意的地方是尽量要用指针访问 内部资源. 这样的话,即使*MyFile对象忘记释放,或者是被别的对象无意中覆盖,也可以保证内部的文件资源可以正确释放.

总结

Go语言是短小精悍的语言,它的设计哲学来自UNIX和C语言的KISS原则. 但是Go语言的语法规范虽然很少(50+页),但是却提供了无限可能的组合方式.

Go语言之父Rob Pike有篇文章少是指数级的多. 但是为什么少就是多呢?

参考下数学公理就明白了: 数学的基础规则是很简单的,但是组合方式却是无穷的. Go语言的思路也是提供虽然少但却是正交的基础特性,通过不同特性的无穷的 组合方式来应对各种问题(一个反例就是C++的构造函数和析构函数).

这里我们主要是基于Go语言的deferruntime.SetFinalizer两个基础特性,来解决资源的自动回收问题.

猜你在找的Go相关文章