Kalau kebutuhan kita hanya “jalankan pekerjaan ini di thread lain, lalu ambil hasilnya”, menulis std::thread + promise + future secara manual terasa panjang. std::async membungkus ketiganya menjadi satu panggilan: beri tugasnya, terima std::future, panggil get() — dan hasilnya terambil dengan menunggu otomatis. Di balik kesederhanaan itu ada satu keputusan yang perlu kita pahami: launch policy — pekerjaan dijalankan segera di thread tersendiri (std::launch::async), atau ditunda sampai hasilnya benar-benar diminta (std::launch::deferred). Chapter ini menguji keduanya plus policy default, lalu menunjukkan exception yang menyeberang antar thread tanpa kode tambahan.
Launch Policy: async vs deferred #
Dua policy yang bisa dipilih:
std::launch::async: tugas dijalankan SEGERA di thread baru. Panggilanget()kemudian menunggu sampai selesai.std::launch::deferred: tugas TIDAK dijalankan dulu; ia baru dieksekusi saatget()(atauwait()) dipanggil — dan dieksekusi di thread pemanggil, layaknya pemanggilan fungsi biasa yang ditunda.
#include <future>
#include <iostream>
#include <stdexcept>
int jumlahAngka(int n) {
int total = 0;
for (int i = 1; i <= n; ++i) {
total += i;
}
return total;
}
int main() {
// 1) policy default (async | deferred): keputusan diserahkan ke implementasi
std::future<int> f0 = std::async(jumlahAngka, 10);
std::cout << "default : " << f0.get() << "\n";
// 2) std::launch::async: tugas dijalankan di thread tersendiri
std::future<int> f1 = std::async(std::launch::async, jumlahAngka, 100);
std::cout << "async : " << f1.get() << "\n";
// 3) std::launch::deferred: dieksekusi malas, baru jalan saat get()
std::future<int> f2 = std::async(std::launch::deferred, jumlahAngka, 100);
std::cout << "deferred: " << f2.get() << "\n";
// 4) exception di dalam tugas diantar future ke pemanggil
std::future<int> f3 = std::async(std::launch::async, []() -> int {
throw std::runtime_error("kerja gagal");
});
try {
f3.get();
} catch (const std::exception& e) {
std::cout << "exception: " << e.what() << "\n";
}
return 0;
}Output yang Dihasilkan:
default : 55
async : 5050
deferred: 5050
exception: kerja gagalBedah barisnya:
- Baris 15-16 (policy default):
std::async(f, args)tanpa policy memakaistd::launch::async | std::launch::deferred— implementasi bebas memilih salah satu. Nilai 55 = jumlah 1..10. - Baris 19-20 (
launch::async):jumlahAngka(100)mulai dihitung di thread lain SEKETIKA;get()menunggu bila belum selesai. Hasilnya 5050. - Baris 23-24 (
launch::deferred): sampai barisget(), tugas belum menyentuh CPU sama sekali.get()menjalankannya langsung di threadmain()— hasilnya tetap 5050, tetapi tidak ada paralelisme yang terjadi. - Baris 27-29 (tugas melempar exception): exception di dalam tugas async ditangkap pustaka, disimpan di dalam state bersama, dan dilempar ulang TEPAT di baris
get(). - Baris 31-33 (penerimaan):
f3.get()melemparstd::runtime_error("kerja gagal")di threadmain(), yang kita tangkap seperti exception biasa. Tidak ada kode pengiriman yang perlu ditulis — mirip mekanismeset_exceptionpada chapter future dan promise, otomatis.
Catatan Portabilitas Policy Default #
Policy default async | deferred adalah sumber kejutan portabilitas yang klasik. Beberapa implementasi memilih menjalankan tugas segera; implementasi lain (dan kondisi beban sistem tertentu) memilih menunda. Akibatnya, kode yang bergantung pada “pasti sudah berjalan sebelum baris berikutnya” bisa berperilaku berbeda antar platform — atau antar versi pustaka yang sama. Aturan praktisnya tegas:
Kelalaian yang Sering Terjadi #
Ada satu jebakan yang wajib kita kenal: std::future yang dikembalikan std::async dengan policy async memiliki destruktor khusus — bila future itu dibuang tanpa pernah di-get()/wait(), destruktor MENUNGGU tugasnya selesai. Artinya kode seperti std::async(std::launch::async, kerja); // hasil dibuang tetap memblokir satu baris penuh, dan pekerjaan yang kelihatannya berjalan paralel sebenarnya menyatu kembali seketika. Selalu simpan future yang dikembalikan std::async, dan get() saat memang dibutuhkan.
Dengan std::async, membuka pekerjaan lalu menunggu hasilnya tinggal dua baris. Namun perhatikan betapa banyak detail hidup thread yang masih harus kita urus manual — join(), sinyal penghentian. C++20 menyederhanakan semuanya dengan std::jthread dan std::stop_token, dan itu topik chapter berikutnya.
Referensi: cppreference — std::async , cppreference — std::future .