Episode ini membuka multithreading dengan pthreads, synchronization primitives seperti mutex, semaphore, dan condition variable, bahaya shared memory, race conditions, dan cara menghindari deadlock, serta pengenalan OpenMP untuk parallel loops.

Prosesor modern punya banyak inti, dan program yang memakai satu inti saja menyia-nyiakan sebagian besar kekuatannya. Episode 16 membahas concurrency: cara menjalankan banyak tugas bersamaan memakai thread, dan cara mengoordinasikan mereka tanpa bentrok.
Pustaka POSIX Threads atau pthreads adalah cara standar menulis program multithread di C. Bersamanya datang tanggung jawab besar: thread yang berbagi data tanpa sinkronisasi akan menghasilkan race condition yang memicu bug paling sulit dilacak.
Paruh kedua episode ini memperkenalkan OpenMP, yang mengubah loop menjadi paralel hanya dengan satu direktif — cara cepat mendapatkan kecepatan tanpa menulis manajemen thread manual.
Thread dibuat dengan pthread_create dan ditunggu dengan pthread_join. Setiap thread menjalankan fungsi yang menerima argumen:
cat > thread.c <<'EOF'
#include <pthread.h>
#include <stdio.h>
void *tugas(void *arg) {
int id = *(int *)arg;
printf("thread %d berjalan\n", id);
return NULL;
}
int main(void) {
pthread_t t;
int id = 1;
pthread_create(&t, NULL, tugas, &id);
pthread_join(t, NULL);
return 0;
}
EOF
gcc -Wall -Wextra -pthread thread.c -o thread && ./threadpthread_create(&t, NULL, tugas, &id) memulai thread baru yang menjalankan fungsi tugas dengan argumen &id. Bendera -pthread saat kompilasi wajib — ia menautkan library thread dan mendefinisikan macro yang dibutuhkan. pthread_join menunggu thread selesai sebelum program berakhir.
Thread dalam satu proses berbagi memori yang sama. Variabel global bisa dibaca dan ditulis semua thread. Kemudahan ini adalah sumber masalah terbesar: dua thread menulis variabel yang sama secara bersamaan menghasilkan hasil yang tidak menentu.
Mutex memastikan hanya satu thread yang masuk ke bagian kritis pada satu waktu:
#include <pthread.h>
#include <stdio.h>
pthread_mutex_t kunci = PTHREAD_MUTEX_INITIALIZER;
long counter = 0;
void *naikkan(void *arg) {
for (int i = 0; i < 100000; i++) {
pthread_mutex_lock(&kunci);
counter++;
pthread_mutex_unlock(&kunci);
}
return NULL;
}Pola pthread_mutex_lock(&kunci); ...; pthread_mutex_unlock(&kunci) mengunci bagian kritis. Tanpa mutex, dua thread menambah counter bersamaan bisa kehilangan pembaruan. Kunci sesingkat mungkin: kunci terlalu lama membuat thread lain menganggur, dan kunci yang tidak konsisten menyebabkan race condition.
Semaphore menghitung sumber daya yang tersedia, misalnya membatasi jumlah koneksi aktif. Condition variable memungkinkan thread menunggu sampai suatu kondisi terpenuhi: satu thread menandai pthread_cond_signal, yang lain menunggu di pthread_cond_wait. Pola ini dipakai untuk queue pekerja — thread menunggu hingga ada pekerjaan baru, lalu memprosesnya.
Race condition terjadi saat hasil bergantung pada urutan eksekusi antar thread yang tidak terkontrol. Deteksi memakai ThreadSanitizer saat pengembangan:
gcc -g -fsanitize=thread -pthread program.c -o program
./programBendera -fsanitize=thread menyuntikkan pemeriksaan yang melaporkan setiap data race secara detail. Seperti AddressSanitizer di episode 11, alat ini menemukan apa yang tidak terlihat mata dan hanya aktif saat pengembangan.
Deadlock terjadi ketika dua thread saling menunggu kunci yang dipegang yang lain. Aturan pencegahan yang efektif: kunci selalu diambil dalam urutan yang sama di seluruh program, dan hindari memegang satu kunci sambil menunggu kunci lain. Jika memungkinkan, gunakan satu mutex per unit data dan dokumentasikan urutan pengunciannya.
OpenMP memparalelkan loop dengan pragma sederhana. Bangun dengan bendera -fopenmp:
#include <omp.h>
#include <stdio.h>
int main(void) {
long total = 0;
#pragma omp parallel for reduction(+:total)
for (int i = 1; i <= 1000000; i++) {
total += i;
}
printf("total: %ld\n", total);
return 0;
}
EOF
gcc -Wall -Wextra -fopenmp omp.c -o omp && ./ompDirektif #pragma omp parallel for reduction(+:total) membagi iterasi loop ke thread yang tersedia, lalu reduction menjumlahkan hasil parsial tanpa race condition. OpenMP menangani pembuatan thread, pembagian kerja, dan penggabungan hasil secara otomatis.
OpenMP menambahkan kecepatan hanya jika loop aman diparalelkan: tidak ada dependency antar iterasi. Untuk iterasi yang bergantung pada hasil iterasi sebelumnya, paralelisasi akan salah. Ukur speedup dengan omp_get_wtime dan bandingkan versi serial dan paralel.
Warning
Concurrency adalah domain dengan bug paling sulit. Mulai dari kode serial yang benar, lalu tambahkan thread dengan sinkronisasi minimal, dan selalu uji dengan ThreadSanitizer. Kecepatan tidak berarti apa-apa jika hasilnya salah.
Inti yang harus dibawa pulang:
Di episode 17 selanjutnya kita akan membahas advanced memory techniques — memory alignment, padding, dan pointer arithmetic, custom allocators dan memory pool dasar, memory sanitizer dan leak detection, hingga menulis reliable low-level code untuk embedded dan real-time.