Belajar C Language - Concurrency & Parallel Programming
Episode 16 of 24

Belajar C Language - Concurrency & Parallel Programming

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.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Pendahuluan

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.

Multithreading dengan pthreads

Membuat dan Menggabungkan Thread

Thread dibuat dengan pthread_create dan ditunggu dengan pthread_join. Setiap thread menjalankan fungsi yang menerima argumen:

Thread pertama
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 && ./thread

pthread_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.

Berbagi Data Antar Thread

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.

Synchronization Primitives

Mutex: Kunci Eksklusif

Mutex memastikan hanya satu thread yang masuk ke bagian kritis pada satu waktu:

Mutex melindungi counter
#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 dan Condition Variable

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 Conditions dan Deadlock

Mengenali dan Menghindari Race Condition

Race condition terjadi saat hasil bergantung pada urutan eksekusi antar thread yang tidak terkontrol. Deteksi memakai ThreadSanitizer saat pengembangan:

Deteksi race condition
gcc -g -fsanitize=thread -pthread program.c -o program
./program

Bendera -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.

Menghindari Deadlock

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.

Pengenalan OpenMP

Directive Parallel Loop

OpenMP memparalelkan loop dengan pragma sederhana. Bangun dengan bendera -fopenmp:

Parallel loop dengan OpenMP
#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 && ./omp

Direktif #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.

Menjaga Correctness

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.

Penutup

Inti yang harus dibawa pulang:

  • pthread_create memulai thread dan pthread_join menunggunya.
  • Kompilasi pthreads wajib memakai bendera -pthread.
  • Mutex melindungi bagian kritis dari akses bersamaan.
  • Semaphore membatasi sumber daya, condition variable menunggu kondisi.
  • Race condition dideteksi dengan ThreadSanitizer.
  • OpenMP memparalelkan loop yang bebas dependency dengan satu pragma.

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.

Belajar C Language - Concurrency & Parallel Programming | Belajar C Language