ホーム プログラミング言語 golang golang func Go 言語の defer (遅延実行ステートメント)






Go 言語の defer (遅延実行ステートメント)




 
 
Go 言語の defer 文は後続の文の処理を遅延させ、defer が属する関数が戻ろうとしたときに、遅延した処理文が defer の逆の順序、つまり defer になっている文を実行します。 first will run last. defer last のステートメントが最初に実行されます。

 

キーワード defer の使用法は、オブジェクト指向プログラミング言語JavaおよびC#のfinally ステートメント ブロックに似ています。一般的に、割り当てられたリソースの一部を解放するために使用されます。典型的な例は、ミューテックスのロックを解除したり、ファイルを閉じたりすることです。

複数の遅延実行ステートメントの処理順序

複数の遅延動作が登録されている場合、それらは逆の順序で実行されます (スタックと同様、つまり、後入れ先出し)。次のコードは、以下に示すように、一連の数値 print ステートメントを順番に遅延させます。

 package main

import (
    "fmt"
)

func main() {

    fmt.Println("defer 開始")

    // deferを遅延呼び出しスタックに入れる
    defer fmt.Println(1)

    defer fmt.Println(2)

    // 最後に入れたものを一番上にする。先に呼ばれる。
    defer fmt.Println(3)

    fmt.Println("defer 終了")
} 

コード出力は次のとおりです。

defer begin
defer end
3
2
1

結果は次のように分析されます。

  • コードの遅延順序は、最終的な実行順序の逆になります。
  • 遅延呼び出しは、defer が配置されている関数の最後で実行されます。関数の終了は、関数が正常に戻ったとき、またはダウンタイムが発生したときになります。

遅延実行ステートメントを使用して関数終了時にリソースを解放する

ファイルの開閉、リクエストの受信と応答、ロックとロック解除など、ビジネスやロジックでペアの操作を扱うのは面倒です。これらの操作のうち、最も見落としやすいのは、各関数の終了時にリソースを適切に解放して閉じることです。

defer ステートメントは関数が終了するときにたまたま実行されるため、defer を使用するとリソースの解放を非常に便利に処理できます。

1) 遅延同時ロック解除を使用する

次の例では、マップは関数内で同時に使用されます。競合状態を防ぐには、ロックに sync.Mutex を使用します。次のコードを参照してください。

 // 変数
var (
    // デモ用のマップ
    valueByKey = make(map[string]int)
    // マップを安全に同時に使用するためのミューテックス
    valueByKeyGuard sync.Mutex
)

// キーに対応する値を読み取る
func readValue(key string) int {
    // 共有リソースに対してロックをかける
    valueByKeyGuard.Lock()
    // 値を読み取る
    v := valueByKey[key]
    // 共有リソースのロックを解放する
    valueByKeyGuard.Unlock()
    // 値を返す
    return v
} 

コードの説明は次のとおりです。

  • 3 行目ではマップがインスタンス化され、キーは文字列型、値は int です。
  • 5 行目、マップはデフォルトでは同時安全ではありません。マップ アクセスを保護するために sync.Mutex ミューテックスを準備します。
  • 9 行目では、readValue() 関数にキーが与えられ、マップから値を取得した後に戻ります。この関数は同時環境で使用されるため、同時実行の安全性を確保する必要があります。
  • 11 行目、ミューテックスを使用してロックします。
  • 13 行目、マップから値を取得します。
  • 15 行目で、ミューテックスを使用してロックを解除します。
  • 17行目は取得したマップ値を返します。

defer ステートメントを使用して上記のステートメントを簡素化します。次のコードを参照してください。

 func readValue(key string) int {

    valueByKeyGuard.Lock()
   
    // defer後面の言葉はすぐに実行されず、関数の終了時に実行されます。
    defer valueByKeyGuard.Unlock()

    return valueByKey[key]
} 

上記コードの 6 行目から 8 行目は、以前のコードを修正および追加したコードであり、コードの説明は次のとおりです。

  • 6 行目では、ミューテックスがロックされた後、defer ステートメントを使用してロックを解除します。このステートメントはすぐには実行されませんが、readValue() 関数が返されたときに実行されます。
  • 8行目はマップから値を問い合わせて返す処理ですが、ミューテックスを使わない場合の書き方と同じですが、上記のコードに比べてこちらの方が簡単です。

2) 遅延リリース ファイル ハンドルを使用する

ファイルの操作は、ファイルを開く、ファイルリソースの取得と操作、リソースを閉じるといういくつかのプロセスを経る必要があり、操作が完了した後にファイルリソースを閉じないと、プロセスはファイルを解放できません。ファイルリソース. 次の例では、ファイル名に従ってファイルが取得されます. 関数のサイズ, 関数はファイルを開き、ファイルサイズを取得し、ファイルを閉じる必要があります. システム動作の各ステップであるためエラー処理が必要であり、処理の各ステップが終了の可能性を引き起こすため、終了時にリソースを解放する必要があります。また、関数の終了時にファイル リソースを正しく解放することに細心の注意を払う必要があります。次のコードを参照してください。

 // ファイル名からファイルサイズを取得する関数
func fileSize(filename string) int64 {

    // ファイル名を指定してファイルハンドルを取得し、エラーを返す
    f, err := os.Open(filename)

    // エラーが発生した場合、ファイルサイズ0を返す
    if err != nil {
        return 0
    }

    // ファイルのステータス情報を取得する
    info, err := f.Stat()

    // 情報の取得に失敗した場合、ファイルを閉じてファイルサイズ0を返す
    if err != nil {
        f.Close()
        return 0
    }

    // ファイルサイズを取得する
    size := info.Size()

    // ファイルを閉じる
    f.Close()

    // ファイルサイズを返す
    return size
} 

コードの説明は次のとおりです。

  • 2 行目はファイル サイズを取得する関数を定義しており、戻り値は 64 ビットのファイル サイズ値です。
  • 5行目では、osパッケージが提供する関数Open()を使用して、指定されたファイル名に従ってファイルを開き、ファイルの操作に使用したハンドルと操作エラーを返します。
  • 8行目、オープン処理中にファイルが見つからない、ファイルが占有されているなどのエラーが発生した場合、ファイルサイズは0として返されます。
  • 13行目では、ファイルハンドルfは現時点では正常に使用できますが、fのメソッドStat()を使ってファイルの情報を取得していますが、情報取得時にエラーが発生する場合もあります。
  • 行 16 ~ 19 はエラーを処理します。この時点では、ファイルは正常に開かれます。リソースを解放するには、 f の Close() メソッドを呼び出してファイルを閉じる必要があります。そうしないと、リソース リークが発生します。
  • 22行目、ファイルサイズを取得します。
  • 25 行目はファイルを閉じてリソースを解放します。
  • 28行目では取得したファイルサイズを返します。

上の例では、25 行目はファイルを閉じる操作です。以下では、コードを簡素化するために defer を使用しています。コードは次のとおりです。

 func fileSize(filename string) int64 {

    f, err := os.Open(filename)

    if err != nil {
        return 0
    }

    // 延期にCloseを呼び出し、この時Closeは呼び出されない
    defer f.Close()

    info, err := f.Stat()

    if err != nil {
        // 延期機構がトリガーされて、Closeがファイルを閉じる
        return 0
    }

    size := info.Size()

    // 延期機構がトリガーされて、Closeがファイルを閉じる
    return size
} 

コードの太字部分は以前のコードと比較して変更された部分であり、コードの説明は次のとおりです。

  • 10 行目では、ファイルが正常に開かれた後、defer を使用して f.Close() の呼び出しを遅らせます。このコード文を 4 行目の空白行に置くことはできないことに注意してください。ファイルが正しく開かれないと、f は遅延後、ステートメントが起動すると、クラッシュ エラーがトリガーされます。
  • 16 行目と 22 行目、defer の後のステートメント (f.Close()) は、関数が戻ってリソースを自動的に解放する前に呼び出されます。
 

「 Go 言語の defer (遅延実行ステートメント)」についてわかりやすく解説!絶対に観るべきベスト2動画

defer + recover を使用して Go でパニックを処理する
17 Golang Tutorial – Defer and File System