1 Pengenalan fitur defer
dalam Golang
Dalam bahasa Go, pernyataan defer
menunda eksekusi pemanggilan fungsi yang mengikutinya hingga fungsi yang berisi pernyataan defer
hampir selesai dieksekusi. Anda dapat menganggapnya sebagai blok finally
dalam bahasa pemrograman lain, namun penggunaan defer
lebih fleksibel dan unik.
Manfaat menggunakan defer
adalah dapat digunakan untuk melakukan tugas-tugas pembersihan, seperti menutup file, membuka kunci mutex, atau hanya mencatat waktu keluar dari suatu fungsi. Hal ini dapat membuat program lebih andal dan mengurangi jumlah pekerjaan pemrograman dalam penanganan pengecualian. Dalam filosofi desain Go, penggunaan defer
direkomendasikan karena membantu menjaga kode yang ringkas dan mudah dibaca saat menangani kesalahan, pembersihan sumber daya, dan operasi-operasi berikutnya.
2 Prinsip kerja defer
2.1 Prinsip kerja dasar
Prinsip kerja dasar dari defer
adalah menggunakan tumpukan (prinsip terakhir masuk, pertama keluar) untuk menyimpan setiap fungsi tertunda yang akan dieksekusi. Ketika pernyataan defer
muncul, bahasa Go tidak segera mengeksekusi fungsi yang mengikutinya. Sebagai gantinya, ia memasukkannya ke dalam tumpukan yang didedikasikan. Hanya ketika fungsi luar hampir selesai mengembalikan nilai, fungsi-fungsi tertunda ini akan dieksekusi sesuai urutan tumpukan, dengan fungsi dalam pernyataan defer
yang terakhir dinyatakan dieksekusi terlebih dahulu.
Selain itu, perlu dicatat bahwa parameter dalam fungsi-fungsi yang mengikuti pernyataan defer
dihitung dan ditetapkan pada saat pernyataan defer
dinyatakan, bukan pada saat eksekusi sebenarnya.
func contoh() {
defer fmt.Println("dunia") // tertunda
fmt.Println("halo")
}
func utama() {
contoh()
}
Kode di atas akan menghasilkan output:
halo
dunia
dunia
dicetak sebelum fungsi contoh
keluar, meskipun muncul sebelum halo
dalam kode.
2.2 Urutan eksekusi dari beberapa pernyataan defer
Ketika sebuah fungsi memiliki beberapa pernyataan defer
, mereka akan dieksekusi dengan prinsip terakhir masuk, pertama keluar. Hal ini sering sangat penting untuk memahami logika pembersihan yang kompleks. Berikut adalah contoh urutan eksekusi dari beberapa pernyataan defer
:
func beberapaDefer() {
defer fmt.Println("tunda pertama")
defer fmt.Println("tunda kedua")
defer fmt.Println("tunda ketiga")
fmt.Println("tubuh fungsi")
}
func utama() {
beberapaDefer()
}
Output dari kode ini akan menjadi:
tubuh fungsi
tunda ketiga
tunda kedua
tunda pertama
Karena defer
mengikuti prinsip terakhir masuk, pertama keluar, meskipun "tunda pertama" adalah yang pertama ditunda, ia akan dieksekusi terakhir.
3 Aplikasi defer
dalam berbagai skenario
3.1 Pelepasan sumber daya
Dalam bahasa Go, pernyataan defer
umumnya digunakan untuk menangani logika pelepasan sumber daya, seperti operasi-operasi file dan koneksi basis data. defer
memastikan bahwa setelah eksekusi fungsi, sumber daya yang sesuai akan dilepaskan dengan benar tanpa memandang alasan keluar dari fungsi.
Contoh operasi file:
func BacaFile(namafile string) {
file, err := os.Open(namafile)
if err != nil {
log.Fatal(err)
}
// Gunakan defer untuk memastikan file ditutup
defer file.Close()
// Lakukan operasi pembacaan file...
}
Pada contoh ini, begitu os.Open
berhasil membuka file, pernyataan defer file.Close()
yang mengikuti memastikan bahwa sumber daya file akan ditutup dengan benar dan sumber daya handle file akan dilepaskan ketika fungsi berakhir.
Contoh Koneksi Basis Data:
func TanyaBasisData(query string) {
db, err := sql.Open("mysql", "pengguna:katasandi@/namabasisdata")
if err != nil {
log.Fatal(err)
}
// Pastikan koneksi basis data ditutup menggunakan defer
defer db.Close()
// Lakukan operasi kueri basis data...
}
Demikian pula, defer db.Close()
memastikan bahwa koneksi basis data akan ditutup ketika keluar dari fungsi TanyaBasisData
, tanpa memandang alasan (pengembalian normal atau pengecualian dilemparkan).
3.2 Operasi Kunci pada Pemrograman Bersamaan
Dalam pemrograman bersamaan, menggunakan defer
untuk menangani pelepasan kunci mutex adalah praktik yang baik. Hal ini memastikan bahwa kunci dilepaskan dengan benar setelah eksekusi kode bagian kritis, sehingga menghindari deadlock.
Contoh Penggunaan Kunci Mutex:
var mutex sync.Mutex
func updateSharedResource() {
mutex.Lock()
// Gunakan defer untuk memastikan bahwa kunci dilepaskan
defer mutex.Unlock()
// Lakukan modifikasi pada sumber daya bersama...
}
Tidak peduli apakah modifikasi sumber daya bersama berhasil atau terjadi panic di antaranya, defer
akan memastikan bahwa Unlock()
dipanggil, memungkinkan goroutine lain yang menunggu kunci untuk mendapatkannya.
Tip: Penjelasan terperinci tentang kunci mutex akan dibahas dalam bab-bab berikutnya. Pemahaman tentang skenario aplikasi defer sudah cukup pada saat ini.
3 Kesalahan Umum dan Pertimbangan untuk defer
Saat menggunakan defer
, meskipun keterbacaan dan kebermaintannya kode sangat ditingkatkan, ada juga beberapa kesalahan umum dan pertimbangan yang perlu diingat.
3.1 Parameter fungsi yang ditunda dievaluasi segera
func printValue(v int) {
fmt.Println("Nilai:", v)
}
func main() {
nilai := 1
defer printValue(nilai)
// Memodifikasi nilai `nilai` tidak akan mempengaruhi parameter yang sudah dilewatkan ke defer
nilai = 2
}
// Output akan menjadi "Nilai: 1"
Meskipun nilai nilai
berubah setelah pernyataan defer
, parameter yang dilewatkan ke printValue
dalam defer
sudah dievaluasi dan tetap, sehingga outputnya tetap "Nilai: 1".
3.2 Hati-hati ketika menggunakan defer di dalam loop
Menggunakan defer
di dalam loop dapat menyebabkan sumber daya tidak dilepaskan sebelum loop berakhir, yang dapat mengakibatkan kebocoran sumber daya atau kehabisan sumber daya.
3.3 Hindari "lepas setelah digunakan" dalam pemrograman konkuren
Dalam program konkuren, saat menggunakan defer
untuk melepaskan sumber daya, penting untuk memastikan bahwa semua goroutine tidak akan mencoba mengakses sumber daya setelah sumber daya tersebut dilepaskan, untuk mencegah race condition.
4. Perhatikan urutan eksekusi pernyataan defer
Pernyataan defer
mengikuti prinsip Last-In-First-Out (LIFO), di mana defer
yang terakhir dinyatakan akan dieksekusi terlebih dahulu.
Solusi dan Praktik Terbaik:
- Selalu sadari bahwa parameter fungsi dalam pernyataan
defer
dievaluasi pada saat deklarasi. - Saat menggunakan
defer
di dalam loop, pertimbangkan menggunakan fungsi anonim atau secara eksplisit memanggil pelepasan sumber daya. - Dalam lingkungan konkuren, pastikan bahwa semua goroutine telah menyelesaikan operasinya sebelum menggunakan
defer
untuk melepaskan sumber daya. - Saat menulis fungsi yang berisi beberapa pernyataan
defer
, pertimbangkan dengan hati-hati urutan eksekusi dan logikanya.
Dengan mengikuti praktik terbaik ini, Anda dapat menghindari sebagian besar masalah yang dihadapi saat menggunakan defer
dan memastikan penulisan kode Go yang lebih tangguh dan mudah dipelihara.