真實世界的 Go 設計模式 - 資源獲取即初始化
RAII,全稱資源獲取即初始化(英語:Resource Acquisition Is Initialization),它是在一些面嚮對象語言中的一種慣用法。RAII 源於 C++,在 Java,C#,D,Ada,Vala 和 Rust 中也有應用。1984-1989 年期間,比雅尼 · 斯特勞斯特魯普和安德魯 · 柯尼希在設計 C++ 異常時,爲解決資源管理時的異常安全性而使用了該用法,後來比雅尼 · 斯特勞斯特魯普將其稱爲 RAII。
主要思想是:
-
在構造函數中申請資源 (acquire resource)
-
在析構函數中釋放資源 (release resource)
這樣就可以把資源的獲取和釋放跟對象的生命週期綁定在一起。
RAII 的主要優點:
-
自動資源管理: 不需要手動釋放資源, 避免資源泄露。
-
異常安全: 即使在異常情況下, 也可以正確釋放資源。
-
避免重複獲取資源: 每個對象代表一個資源, 不會重複獲取。
RAII 的主要作用是在不失代碼簡潔性的同時,可以很好地保證代碼的異常安全性。
下面的 C++ 實例說明了如何用 RAII 訪問文件和互斥量:
#include <string>
#include <mutex>
#include <iostream>
#include <fstream>
#include <stdexcept>
void write_to_file(const std::string & message)
{
// 創建關於文件的互斥鎖
static std::mutex mutex;
// 在訪問文件前進行加鎖
std::lock_guard<std::mutex> lock(mutex);
// 嘗試打開文件
std::ofstream file("example.txt");
if (!file.is_open())
throw std::runtime_error("unable to open file");
// 輸出文件內容
file << message << std::endl;
// 當離開作用域時,文件句柄會被首先析構 (不管是否拋出了異常)
// 互斥鎖也會被析構 (同樣地,不管是否拋出了異常)
}
比如 Rust 中,m.lock()
獲取到鎖,也不需要顯示的釋放鎖:
use std::sync::Mutex;
fn main() {
let m = Mutex::new(5);
{
let mut num = m.lock().unwrap();
*num = 6;
}
println!("m = {:?}", m);
}
Go 語言倒沒有沒有 C++ 那樣的構造函數和析構函數的概念, 但是 Go 中併發原語也算有半個類似 RAII 的技術。
我們知道,Go 標準庫的的併發原語都不需要特別的初始化,我們直接使用它的零值即可,比如:
var lock sync.Mutex
var rwLock sync.RWMutex
var wg sync.WaitGroup
......
但是 Go 並沒有實現自動化的析構函數,對鎖自動釋放,我們一般使用defer
來完成這一步:
lock.Lock()
defer lock.Unlock()
rwlock.RLock()
defer rwlock.RUnlock()
wg.Done()
......
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/MiACdPNOhnXRoysRX2VBVA