/******************************************************************************/ /** @file "Pthreads/C/Primer 5 - cond/condvar1.c" @brief Upotreba uslovnih promenljivih Primer korišćenja uslovnih promenljivih iz POSIX Treads API-ja. Glavna nit kreira tri niti. Dve niti inkrementiraju promenljivu count, dok treća posmatra njenu vrednost. Kada count dostigne predefinisanu granicu treća nit dobija signal od jedne niti (jedna od dve moguće) koja vrši inkrementiranje. Ovaj primer je preuzet iz Pthreads tutorijala sa adrese http://www.llnl.gov/computing/tutorials/pthreads/ i prilagodjen potrebama ovog kursa. Originalni primer se nalazi u knjizi "Pthreads Programming", B. Nichols et al. O'Reilly and Associates. */ /******************************************************************************/ #include #include #include /******************************************************************************/ /** @def NUM_THREADS @brief Koliko niti će se kreirati. */ #define NUM_THREADS 3 /** @def T_COUNT @brief Broj ciklusa. */ #define T_COUNT 10 /** @def COUNT_LIMIT @brief Predefinisana gornja granica. */ #define COUNT_LIMIT 12 /******************************************************************************/ /** @var count @brief brojač čija granicna vrednost se čeka */ int count = 0; /** @var thread_ids @brief brojevi niti koji će biti dodeljeni prilikom poziva */ int thread_ids[3] = {0, 1, 2}; /** @var count_mutex @brief medjusobno isključenje brojača */ pthread_mutex_t count_mutex; /** @var count_threshold_cv @brief blokiranje niti koja čeka da brojač dostigne odgovarajuću vrednost */ pthread_cond_t count_threshold_cv; /******************************************************************************/ /** @brief Posao za niti koje inkrementiraju promenljivu count. @param idp Generički pokazivač na broj niti. @return Vraća rezultat izvršavanja funkcije pthread_exit. Pre ulaska u kritičnu sekciju nit ce zaključati mutex, kako deljenim resursima ne bi pristupila ni jedna druga nit. Po inkrementiranju vrednosti promenljive count, ispita se njena vrednost. Ukoliko je dostignuta gornja granica šalje se signal onoj niti koja čeka na pristup resursima. Po izlasku iz kritične sekcije otključava se mutex. Dodatno, svaka nit obavlja neki beskorisni posao, kako bi ostale niti mogle da se smenjuju na bravi. */ void* inc_count(void* idp) { int j, i; double result = 0.0; int* my_id = idp; for (i = 0; i < T_COUNT; i++) { // Ulazimo u kriticnu sekciju - zakljucaj bravu. pthread_mutex_lock(&count_mutex); count++; // Proveri vrednost promenljive count i, ako je ispunjen uslov, // signalizira se nit koja ceka. Treba obratiti panju na to da // se sve ovo desava dok je mutex zakljucen. if (count == COUNT_LIMIT) { pthread_cond_signal(&count_threshold_cv); printf("inc_count(): thread %d, count = %d Threshold reached.\n", *my_id, count); } printf("inc_count(): thread %d, count = %d, unlocking mutex\n", *my_id, count); pthread_mutex_unlock(&count_mutex); for (j = 0; j < 1000; j++) result = result + (double) rand(); } pthread_exit(NULL); } /******************************************************************************/ /** @brief Posao za nit koja čeka. @param idp Generički pokazivač na broj niti. @return void* Povratna vrednost funkcije. Sve što ova nit treba da radi jeste da zaključa mutex i da čeka signal. Treba obratiti pažnju na to da rutina pthread_cond_wait automatski i atomično otključava mutex dok čeka. Takodje, ako se dostigne @ref COUNT_LIMIT pre nego se pokrene ova rutina od strane niti koja čeka, petlja će biti preskočena , to sprečava ulazak u beskonačnu petlju. */ void* watch_count(void* idp) { int* my_id = idp; printf("Starting watch_count(): thread %d\n", *my_id); // Ulazak u kriticnu sekciju - zakljucaj mutex. pthread_mutex_lock(&count_mutex); while (count < COUNT_LIMIT) { pthread_cond_wait(&count_threshold_cv, &count_mutex); printf("watch_count(): thread %d Condition signal received.\n", *my_id); } // Izlazak iz kriticne sekcije. pthread_mutex_unlock(&count_mutex); pthread_exit(NULL); } /******************************************************************************/ /** @brief Glavni program @param argc Broj argumenata komandne linije @param argv Argumenti komandne linije @return int Rezultat izvšavanja programa Glavni program prvo inicijalizuje mutex i uslovnu promenljivu, zatim eksplicitno, radi portabilnosti inicijalizuje i postavi odredjene atribute. Nakon toga, u odredjenom vremenskom razmaku kreira svaku od pomenutih niti, i to: prvo čekajuće , a zatim i inkrementirajuće. Na kraju, čeka se kraj svih niti pre nego što se glavna nit završi. */ int main(int argc, char* argv[]) { int i, j = rand() * rand() * rand(); pthread_t threads[3]; pthread_attr_t attr; #ifdef WIN32 pthread_win32_process_attach_np(); #endif pthread_mutex_init(&count_mutex, NULL); pthread_cond_init (&count_threshold_cv, NULL); // Zbog portabilnosti, dobro je eksplicitno kreirati niti sa joinable // artibutom. pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); pthread_create(&threads[2], &attr, watch_count, (void*) &thread_ids[2]); for(i = 0; i < j; i++) i++; pthread_create(&threads[0], &attr, inc_count, (void*) &thread_ids[0]); pthread_create(&threads[1], &attr, inc_count, (void*) &thread_ids[1]); for (i = 0; i < NUM_THREADS; i++) pthread_join(threads[i], NULL); printf ("Main(): Waited on %d threads. Done.\n", NUM_THREADS); pthread_attr_destroy(&attr); pthread_mutex_destroy(&count_mutex); pthread_cond_destroy(&count_threshold_cv); pthread_exit (NULL); #ifdef WIN32 pthread_win32_process_detach_np(); #endif } /******************************************************************************/