390 likes | 509 Vues
This unit delves into the intricacies of POSIX threads programming, emphasizing the essential techniques for protecting shared resources from concurrent thread access. You'll learn how to utilize mutexes to prevent data races and manage execution timing of threads. The unit provides hands-on exercises, including code examples that highlight handling global and thread-specific data, implementing condition variables, and mastering thread execution control. By the end, you'll be equipped with practical skills for robust multi-threaded programming in AIX environments.
E N D
第十一单元 线程:小心你的邻居 中山大学-AIX应用程序开发
What This Unit is About • This lecture introduces the concepts and more specific calls to handle • the real world of POSIX threads programming. In the real world, a • programmer needs to worry about protecting data resources from • simultaneous thread updates. A programmer may also be interested in • controlling when a given thread executes and how fast it executes. • What You Should Be Able to Do • After completing this unit, you should be able to: • Protect data resources from multi-thread usage. • Control thread execution. • How You Will Check Your Progress • You can check your progress by completing: • Lab exercise 12 • References: • AIX 5L online documentation
Unit Objectives • Protect data resources from multithread usage. • Control thread execution
Handling Global Data Example (1 of 2) /* global.c */ #include <stdio.h> #include <stdlib.h> #include <pthread.h> int g_count; int g_done; pthread_mutex_t g_count_mutex; void * callFunc(void * notused) { int i; printf("I'm in the child thread now....\n"); for(i=0;i<10;i++) { pthread_mutex_lock(&g_count_mutex); g_count++; pthread_mutex_unlock(&g_count_mutex); printf("Count in CHILD is %d\n", g_count); sleep(1); } g_done = 1; /* Child is done with the mutex */ pthread_exit(NULL); }
Handling Global Data Example (1 of 2) main() { pthread_t callThd; int ret; int i; g_count=0; g_done=0; pthread_mutex_init(&g_count_mutex,NULL); ret=pthread_create(&callThd, NULL, callFunc, NULL); if(ret) { printf("problems on creating thread\n"); exit(EXIT_FAILURE); } for(i=0;i<10;i++) { pthread_mutex_lock(&g_count_mutex); g_count++; pthread_mutex_unlock(&g_count_mutex); printf("Count in PARENT is %d\n", g_count); sleep(1); } while (!g_done) sleep(1); pthread_mutex_destroy(&g_count_mutex); pthread_exit(NULL); }
Thread Specific Data: Example (1 of 2) /* tsd.c */ #include <pthread.h> pthread_key_t count_key; void * thread_starter(void * TID) { int * ptr; int ThreadID; ThreadID=*(int *)TID; ptr=(int *) malloc(sizeof(int)); pthread_setspecific(count_key, ptr); *ptr=0; (*ptr)++; subfunc(); (*ptr)++; printf("Thread %d's count should be 3 now. The pointer says:%d\n",ThreadID, *ptr); *(int *)TID = 0; subfunc(void) int * local_p; local_p=pthread_getspecific(count_key); (*local_p)++; }
Thread Specific Data: Example (2 of 2) void count_key_destructor(void * Data) { free(Data); } main() { pthread_t thd1; pthread_t thd2; int tid1=1, tid2=2; pthread_key_create(&count_key, count_key_destructor); pthread_create(&thd1, NULL, thread_starter, (void *)&tid1); pthread_create(&thd2, NULL, thread_starter, (void *)&tid2); while (tid1 != 0 || tid2 != 0) sleep(1); while(pthread_key_delete(count_key) != 0) sleep(1); pthread_exit(NULL); }
Handling Logic Static Data BEFORE funca() { static int count = 0; count++; printf("count is %d\n", count); } main() { funca(); funca(); } AFTER funca(short funca_count) { printf("count is %d\n", funca_count); } main() { short count = 1; funca(count); count++; funca(count); }
线程安全函数和可重入函数 • 线程安全函数 • 可重入函数
Controlling Execution • Create • Exit • Wait/Interrupt • Cancel • Schedule • Other
Condition Wait Example (1 of 2) /* condwait.c */ #include <signal.h> #include <stdio.h> #include <pthread.h> int Ready=0; pthread_mutex_t m; pthread_cond_t cv; void * slave(void * threadID) { int threadNum = *(int *) threadID; pthread_mutex_lock(&m); while(Ready != 1) pthread_cond_wait(&cv, &m); pthread_mutex_unlock(&m); printf("Thread %d continuing.... \n",threadNum); *(int *)threadID = 0; pthread_exit(NULL); } main() { pthread_t thd1; pthread_t thd2; pthread_t thd3; int j1=1, j2=2, j3=3; pthread_mutex_init(&m,NULL); pthread_cond_init(&cv,NULL); pthread_create(&thd1, NULL, slave, (void * )&j1); pthread_create(&thd2, NULL, slave, (void * )&j2); pthread_create(&thd3, NULL, slave, (void * )&j3);
printf("Main doing work....\n"); sleep(5); /* Simulate work */ pthread_mutex_lock(&m); Ready=1; pthread_cond_broadcast(&cv); pthread_mutex_unlock(&m); printf("Main ready for others and continuing on.... \n"); sleep(5); /* Wait for children to terminate * (using pthread_join with undetached * children would be better) */ while ( j1 != 0 || j2 != 0 || j3 != 0 ) sleep(1); pthread_mutex_destroy(&m); pthread_cond_destroy(&cv); pthread_exit((void *) 0); }
Master/Slave Example (1 of 2) /* master.c */ #include <signal.h> #include <stdio.h> #include <pthread.h> int jobInfo[25]; int jobAvail = 0; int jobPtr=0; /* job data passed to thread */ /* Boolean - # jobs available*/ /* job index for slave thread */ pthread_mutex_t m; pthread_cond_t cv; pthread_key_t key; /* used to protect cv */ /* causes slave to wait for new job */ /* provides TSD data for global jobInfo*/ void * slave(void * threadID) { int threadNum=*(int *) threadID; int * local_job; /* will store jobInfo data */ for(;;) { pthread_mutex_lock(&m); while(jobAvail < 1 && jobPtr < 25) pthread_cond_wait(&cv, &m); if (jobPtr == 25) { pthread_mutex_unlock(&m); /* We're done */ break; } local_job = (int *)malloc(sizeof(int)); /* allocate room for jobInfo*/ pthread_setspecific(key, (void *)local_job); *local_job = jobInfo[jobPtr]; /*global to TSD*/ jobPtr++; /* job handled count goes up */ jobAvail--; /* one less job left */ pthread_mutex_unlock(&m); /* Ready for others to continue*/ printf("Thread %d handling jobthreadNum, *local_job); with local_job info of %d\n", } *(int *)threadID = 0; pthread_exit(NULL); }
Master/Slave Example (2 of 2) main() { pthread_t thd1; pthread_t thd2; pthread_t thd3; int j1=1,j2=2,j3=3; int i; /* thread IDs passed to threads */ /* job index for master thread */ pthread_mutex_init(&m,NULL); pthread_cond_init(&cv,NULL); pthread_create(&thd1, NULL, slave, (void * )&j1); pthread_create(&thd2, NULL, slave, (void * )&j2); pthread_create(&thd3, NULL, slave, (void * )&j3); for(i=0;i< 25;i++) /* create 25 jobs */ { printf("Getting a new job\n"); /*simulates a new job created */ jobInfo[i] = i + 100; /* simulates making data */ pthread_mutex_lock(&m); jobAvail++; pthread_cond_signal(&cv); pthread_mutex_unlock(&m); /* new job available */ } /* Help others finish */ /* (some might be blocked on the pthread_cond_wait call) */ while ( j1 != 0 || j2 != 0 || j3 != 0 ) { pthread_mutex_lock(&m); pthread_cond_signal(&cv); pthread_mutex_unlock(&m); sleep(1); } pthread_mutex_destroy(&m); pthread_cond_destroy(&cv); exit(0); }
Signal Management Example (1 of 2) /* sigmgmt.c */ #include <pthread.h> #include <signal.h> void *threadfunc(void * notused) { struct sigset_t set, oset; sigemptyset(&set); sigaddset(&set, SIGQUIT); sigthreadmask(SIG_BLOCK, &set, NULL); for(;;); /* simulate action */ } void * sigwaiter_thread(void *notused) { int sig; int ret; struct sigset_t set, oset; sigemptyset(&set); sigaddset(&set, SIGQUIT); sigthreadmask(SIG_BLOCK, &set, NULL); for(;;) { sigwait(&set,&sig); printf("sigwait() returned signal = %d\n", sig); } }
Signal Management Example (2 of 2) main() { pthread_t waiterthd, thd; int status; struct sigset_t set, oset; struct sigaction mysig; pthread_attr_t attr; /* Block the signals. */ sigemptyset(&set); sigaddset(&set, SIGQUIT); sigthreadmask(SIG_BLOCK, &set, NULL); pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETATCHED); pthread_create(&waiterthd, &attr, sigwaiter_thread, NULL); pthread_create(&thd, &attr, threadfunc, NULL); sleep(2); pthread_join(thd, (void **)&status); printf("Main- check-\n"); }
Cancellation Example (1 of 2) /* pushpop.c */ #include <pthread.h> pthread_mutex_t m; void cleanup_m(void * m) { printf("In cleanup_m handler.\n"); pthread_mutex_unlock((pthread_mutex_t *)m); } void * thread_starter(void * notused) { printf("In thd1.\n"); pthread_mutex_lock(&m); pthread_cleanup_push(cleanup_m, (void *)&m); sleep(1); pthread_testcancel(); /* Cancelation point */ printf("If I reached here, I didn't get canceled.\n"); pthread_mutex_unlock(&m); pthread_cleanup_pop(0); pthread_exit(NULL); }
Cancellation Example (2 of 2) main() { pthread_t thd1; pthread_mutex_init(&m, NULL); pthread_create(&thd1, NULL, thread_starter, NULL); pthread_cancel(thd1); pthread_mutex_destroy(&m); pthread_exit(NULL); }
Scheduling Example (1 of 2) /* thrdsched.c */ #include <pthread.h> void *func1( void *notused) { int priority; struct sched_param sched; pthread_getschedparam(pthread_self(), &priority, &sched); printf("Child thread policy is %d and priority is %d\n", sched.sched_policy, sched.sched_priority); }
Scheduling Example (2 of 2) main() { struct sched_param sched; pthread_attr_t attr; pthread_t thd; printf("Legend:\nPolicy 0 is SCHED_OTHER.\n"); printf("Policy 1 is SCHED_FIFO.\n"); printf("Policy 2 is SCHED_RR.\n\n"); pthread_create( &thd, NULL, func1, NULL); sleep(1); printf("Changing policy of child thread to RR\n"); printf("Changing priority of child thread to 80\n"); pthread_attr_init(&attr); sched.sched_policy = SCHED_RR; sched.sched_priority= 80; pthread_attr_setschedparam(&attr, &sched); pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); pthread_create( &thd, &attr, func1, NULL); pthread_exit(NULL); }
Once Only Initialization • Initialize variable=PTHREAD_ONCE_INIT • Even if many threads call pthread_once(&ariable,routine). • Routine() is called only once. pthread_once(&variable, routine) • is equivalent to: if(variable equals PTHREAD_ONCE_INIT) { routine() change variable }
Once Only Example (1 of 2) /* once.c */ #include <pthread.h> #include <unistd.h> pthread_t thd[2]; static pthread_once_t once = PTHREAD_ONCE_INIT; void init() { pthread_t self; /* Do your initializations here */ /* Check which thread is here */ self = pthread_self(); if(pthread_equal(self,thd[0])) printf("init() - invoked by Thread 1\n"); if(pthread_equal(self,thd[1])) printf("init() - invoked by Thread 2\n"); }
Once Only Example (2 of 2) void * func(void *arg) { pthread_t self; /* init() should be called only once */ (void)pthread_once(&once, init); self = pthread_self(); if(pthread_equal(self,thd[0])) printf("In Thread 1\n"); if(pthread_equal(self,thd[1])) printf("In Thread 2\n"); pthread_exit((void *)1); } main() { pthread_create(&thd[0], NULL, func, 0); pthread_create(&thd[1], NULL, func, 0); pthread_exit( (void **)1); }
Forking Example (1 of 2) /* atfork.c */ #include <pthread.h> #include <sys/types.h> pthread_mutex_t m; void prefork_prepare(void) { printf("prepare\n"); pthread_mutex_lock(&m); } void postfork_parent(void) { printf("parent\n"); pthread_mutex_unlock(&m); } void postfork_child(void) { printf("Child\n"); pthread_mutex_unlock(&m); } void * func(void * arg) { pthread_mutex_lock(&m); sleep(1); printf("In func before unlock\n"); pthread_mutex_unlock(&m); pthread_exit(NULL); }
Forking Example (2 of 2) void * func(void *arg) { pthread_t self; /* init() should be called only once */ (void)pthread_once(&once, init); self = pthread_self(); if(pthread_equal(self,thd[0])) printf("In Thread 1\n"); if(pthread_equal(self,thd[1])) printf("In Thread 2\n"); pthread_exit((void *)1); } main() { pthread_create(&thd[0], NULL, func, 0); pthread_create(&thd[1], NULL, func, 0); pthread_exit( (void **)1); }