安裝man文檔linux
sudo apt-get install glibc-doc sudo apt-get install manpages-posix-dev
ps -Lf pid
,查看指定線程的LWP號。pthread_t pthread_self(void);
- 返回值:成功:0;失敗:無!int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
示例數據庫
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <pthread.h> #include <string.h> void *thread_func(void *arg) { printf("In thread: thread id = %lu, pid = %u\n", pthread_self(), getpid()); return NULL; } int main() { pthread_t tid; int ret; printf("In main1: thread id = %lu, pid = %u\n", pthread_self(), getpid()); ret = pthread_create(&tid, NULL, thread_func, NULL); if(ret != 0){ fprintf(stderr, "pthread_create error:%s\n", strerror(ret)); exit(1); } sleep(1); printf("In main2: thread id = %lu, pid = %u\n", pthread_self(), getpid()); return 0; }
(void *)&i
,將線程主函數內改成i = *((int *)arg)
是否能夠?不能夠。示例編程
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <pthread.h> #include <string.h> void *thread_func(void *arg) { int i = (int)arg; sleep(i); printf("%dth thread: thread id = %lu, pid = %u\n", i+1, pthread_self(), getpid()); return NULL; } int main() { pthread_t tid; int ret, i; for (i = 0; i<5; i++){ ret = pthread_create(&tid, NULL, thread_func, (void *)i); if(ret != 0){ fprintf(stderr, "pthread_create error:%s\n", strerror(ret)); exit(1); } } sleep(i); return 0; }
【練習】:設計程序,驗證線程之間共享全局數據。數組
#include <stdio.h> #include <pthread.h> #include <stdlib.h> #include <unistd.h> int var = 100; void *tfn(void *arg) { var = 200; printf("thread\n"); return NULL; } int main(void) { printf("At first var = %d\n", var); pthread_t tid; pthread_create(&tid, NULL, tfn, NULL); sleep(1); printf("After pthread_create, var = %d\n", var); return 0; }
示例服務器
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <pthread.h> #include <string.h> void *thread_func(void *arg) { int i = (int)arg; printf("%dth thread: thread id = %lu, pid = %u\n", i+1, pthread_self(), getpid()); return NULL; } int main() { pthread_t tid; int ret, i; for (i = 0; i<5; i++){ ret = pthread_create(&tid, NULL, thread_func, (void *)i); if(ret != 0){ fprintf(stderr, "pthread_create error:%s\n", strerror(ret)); exit(1); } } pthread_exit(NULL); }
int pthread_join(pthread_t thread, void **retval);
成功:0;失敗:錯誤號。【練習】:參數retval非空用法。網絡
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <pthread.h> typedef struct{ int a; int b; } exit_t; void *tfn(void *arg) { exit_t * ret; ret = malloc(sizeof(exit_t)); ret->a = 100; ret->b = 300; pthread_exit((void *)ret); } int main(void) { pthread_t tid; exit_t * retval; pthread_create(&tid, NULL, tfn, NULL); //調用pthread_join能夠獲取線程的退出狀態 pthread_join(tid, (void **)&retval); printf("a = %d, b = %d\n", retval->a, retval->b); free(retval); return 0; }
【練習】:使用pthread_join函數將循環建立的多個子線程回收。數據結構
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <pthread.h> int var = 100; void * tfn(void * arg) { int i; i = (int)arg; sleep(i); if(i == 1){ var = 333; printf("var = %d\n", var); return var; } else if (i == 3) { var = 777; printf("I'm %dth pthread, pthread_id = %lu\n var = %d\n", i+1, pthread_self(), var); pthread_exit((void *)var); } else { printf("I'm %dth pthread, pthread_id = %lu\n var = %d\n", i+1, pthread_self(), var); pthread_exit((void *)var); } return NULL; } int main(void) { pthread_t tid[5]; int i; int *ret[5]; for(i = 0; i < 5; i++) pthread_create(&tid[i], NULL, tfn, (void *)i); for(i = 0; i < 5; i++){ pthread_join(tid[i], (void **)&ret[i]); printf("-------%d 's ret = %d\n'", i, (int)ret[i]); } printf("I'm main pthread tid = %lu\t var = %d\n", pthread_self(), var); sleep(i); return 0; }
int pthread_detach(pthread_t thread);
,成功:0;失敗:錯誤號。通常狀況下,線程終止後,其終止狀態一直保留到其它線程調用pthread_join獲取它的狀態爲止。可是線程也能夠被置爲detach狀態,這樣的線程一旦終止就馬上回收它佔用的全部資源,而不保留終止狀態。不能對一個已經處於detach狀態的線程調用pthread_join,這樣的調用將返回EINVAL錯誤。也就是說,若是已經對一個線程調用了pthread_detach就不能再調用pthread_join了。多線程
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <pthread.h> void *tfn(void *arg) { int n = 3; while(n--){ printf("thread count %d\n", n); sleep(1); } return (void *)1; } int main(void) { pthread_t tid; void *tret; int err; #if 0 //經過線程屬性來設置遊離態 pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); pthread_create(&tid, &attr, tfn, NULL); #else pthread_create(&tid, NULL, tfn, NULL); //讓線程分離-----自動退出,無系統殘留資源 pthread_detach(tid); #endif while(1){ err = pthread_join(tid, &tret); printf("------------err = %d\n", err); if(err != 0) fprintf(stderr, "thread_join error : %s\n", strerror(err)); else fprintf(stderr, "thread exit code %d\n", (int)tret); } }
int pthread_cancel(pthread_t thread);
,成功:0;失敗:錯誤號。#define PTHREAD_CANCELED((void *)-1)
。所以當咱們對一個已經被取消的線程使用pthread_join回收時,獲得的返回值爲-1。【練習】:終止線程的三種方法。注意「取消點」的概念。併發
#include <stdio.h> #include <unistd.h> #include <pthread.h> #include <stdlib.h> void *tfn1(void *arg) { printf("thread 1 returning\n"); return (void *)111; } void *tfn2(void *arg) { printf("thread 2 exiting\n"); pthread_exit((void *)222); } void *tfn3(void *arg) { while(1){ //printf("thread 3: I'm going to die in 3 seconds ... \n"); //sleep(1); pthread_testcancel(); //本身添加取消點 } return (void *)666; } int main() { pthread_t tid; void *tret = NULL; pthread_create(&tid, NULL, tfn1, NULL); pthread_join(tid, &tret); printf("thread 1 exit code = %d\n\n", (int)tret); pthread_create(&tid, NULL, tfn2, NULL); pthread_join(tid, &tret); printf("thread 2 exit code = %d\n\n", (int)tret); pthread_create(&tid, NULL, tfn3, NULL); sleep(3); pthread_cancel(tid); pthread_join(tid, &tret); printf("thread 3 exit code = %d\n", (int)tret); }
int pthread_equal(pthread_t t1, pthread_t t2);
進程 線程 fork pthread_create 建立 exit pthread_exit 退出 wait pthread_join 等待 kill pthread_cancel 殺死 getpid pthread_self 取得ID pthread_detach 分離
本節做爲指引性介紹,Linux下線程的屬性是能夠根據實際項目須要進行設置,以前咱們討論的線程都是採用線程的默認屬性,默認屬性已經能夠解決絕大多數開發時遇到的問題。如咱們對程序的性能提出更高的要求,那麼須要設置線程屬性,好比能夠經過設置線程棧的大小來下降內存的使用,增長最大線程個數。
typedef struct{
int etachstate; //線程的分離狀態
int schedpolicy; //線程調度策略
struct sched_param schedparam; //線程的調度參數
int inheritsched; //線程的繼承性
int scope; //線程的做用域
size_t guardsize; //線程棧末尾的警惕緩衝區大小
int stackaddr_set; //線程的棧設置
void* stackaddr; //線程的位置
size_t stacksize; //線程的大小
} pthread_attr_t;函數
線程屬性主要包括以下屬性:做用域(scope)、棧尺寸(stack size)、棧地址(stack address)、優先級(priority)、分離的狀態(detached state)、調度策略和參數(scheduling policy and parameters)。默認的屬性爲非綁定、非分離、缺省的堆棧、與父進程一樣級別的優先級。
int pthread_attr_init(pthread_attr_t *attr);
,成功:0; 失敗:錯誤號。int pthread_attr_destroy(pthread_attr_t *attr);
,成功:0;失敗:錯誤號。int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
int pthread_attr_getdetachstate(pthread_attr_t *attr, int *detachstate);
這裏要注意的一點是,若是設置一個線程爲分離線程,而這個線程運行又很是快,它極可能在pthread_create函數返回以前就終止了,它終止之後就可能將線程號和系統資源移交給其餘的線程使用,這樣調用pthread_create的線程就獲得了錯誤的線程號。要避免這種狀況能夠採起必定的同步措施,最簡單的方法之一是能夠在被建立的線程裏調用pthread_cond_timedwait函數,讓這個線程等待一下子,留出足夠的時間讓函數pthread_create返回。設置一段等待時間,是在多線程編程裏經常使用的方法。可是注意不要使用諸如wait()之類的函數,它們是使整個進程睡眠,並不能解決同步的問題。
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <pthread.h> #include <string.h> void *thread_func(void *arg) { pthread_exit((void *)11); } int main() { pthread_t tid; int ret; pthread_attr_t attr; ret = pthread_attr_init(&attr); if(ret != 0){ fprintf(stderr, "pthread_attr_init error:%s\n", strerror(ret)); exit(1); } pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); ret = pthread_create(&tid, &attr, thread_func, NULL); if(ret != 0){ fprintf(stderr, "pthread_create error:%s\n", strerror(ret)); exit(1); } ret = pthread_join(tid, NULL); if(ret != 0){ fprintf(stderr, "pthread_join error:%s\n", strerror(ret)); exit(1); } pthread_exit((void *)1); return 0; }
int pthread_attr_setstack(pthread_attr_t *attr, void *stackaddr, size_t stacksize);
int pthread_attr_getstack(const pthread_attr_t *attr, void **stackaddr, size_t *stacksize);
#include <stdio.h> #include <pthread.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #define SIZE 0X10000 void *th_fun(void *arg) { while(1) sleep(1); } int main() { pthread_t tid; int err, detachstate, i = 1; pthread_attr_t attr; size_t stacksize; void *stackaddr; pthread_attr_init(&attr); pthread_attr_getstack(&attr, &stackaddr, &stacksize); pthread_attr_getdetachstate(&attr, &detachstate); //默認是分離態 if(detachstate == PTHREAD_CREATE_DETACHED) printf("thread detached\n"); //默認是非分離 else if (detachstate == PTHREAD_CREATE_JOINABLE) printf("thread join\n"); else printf("thread un known\n"); //設置線程分離屬性 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); while(1){ //在堆上申請內存,指定線程棧的起始地址和大小 stackaddr = malloc(SIZE); if(stackaddr == NULL){ perror("malloc"); exit(1); } stacksize = SIZE; //藉助線程的屬性,修改線程棧空間大小 pthread_attr_setstack(&attr, stackaddr, stacksize); err = pthread_create(&tid, &attr, th_fun, NULL); if(err != 0){ printf("%s\n", strerror(err)); exit(1); } printf("%d\n", i++); } pthread_attr_destroy(&attr); }
而,編程中、通訊中所說的同步與生活中你們印象中的同步概念略有差別。「同」字應是指協同、協助、互相配合。主旨在協同步調,按預約的前後次序運行。
所以,全部「多個控制流,共同操做一個共享資源」的狀況,都須要同步。
全部只能從第三點着手解決。使多個線程在訪問共享資源的時候,出現互斥。
所以,即便有了mutex,若是有線程不按規則來訪問數據,依然會形成數據混亂。
int pthread_mutex_init(pthread_mutex_t * restrict mutex, const pthread_mutexattr_t * restrict attr);
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_init(&mutex, NULL);
int pthread_mutex_destroy(pthread_mutex_t *mutex);
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
trylock加鎖失敗直接返回錯誤號(如:EBUSY),不阻塞。
看以下程序:該程序是很是典型的,因爲共享、競爭而沒有加任何同步機制,致使產生於時間有關的錯誤,形成數據混亂。
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <pthread.h> #include <string.h> void *tfn(void *arg) { srand(time(NULL)); while(1){ printf("hello "); //模擬長時間操做共享資源,致使CPU易主,產生與時間有關的錯誤 sleep(rand() % 3); printf("world\n"); sleep(rand() % 3); } return NULL; } int main(void) { pthread_t tid; srand(time(NULL)); pthread_create(&tid, NULL, tfn, NULL); while(1){ printf("HELLO "); sleep(rand() % 3); printf("WORLD\n"); sleep(rand() % 3); } return 0; }
五、main中加pthread_cancel()將子線程取消。
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <pthread.h> #include <string.h> //定義鎖 pthread_mutex_t mutex; void *tfn(void *arg) { srand(time(NULL)); while(1){ //加鎖 pthread_mutex_lock(&mutex); printf("hello "); //模擬長時間操做共享資源,致使CPU易主,產生與時間有關的錯誤 sleep(rand() % 3); printf("world\n"); //解鎖 pthread_mutex_unlock(&mutex); sleep(rand() % 3); //添加檢查點 pthread_testcancel(); } return NULL; } int main(void) { int flag = 5; pthread_t tid; srand(time(NULL)); //鎖初始化 pthread_mutex_init(&mutex, NULL); //mutex = 1 pthread_create(&tid, NULL, tfn, NULL); while(flag--){ //加鎖 pthread_mutex_lock(&mutex); printf("HELLO "); sleep(rand() % 3); printf("WORLD\n"); //解鎖 pthread_mutex_unlock(&mutex); sleep(rand() % 3); } //取消子線程 pthread_cancel(tid); pthread_join(tid, NULL); //鎖銷燬 pthread_mutex_destroy(&mutex); return 0; }
結論:在訪問共享資源前加鎖,訪問結束後當即解鎖。鎖的「粒度」應越小越好。
示例
#include <stdio.h> #include <unistd.h> #include <pthread.h> int counter; pthread_rwlock_t rwlock; void *th_write(void *arg) { int t; int i = (int)arg; while(1){ t = counter; usleep(1000); pthread_rwlock_wrlock(&rwlock); printf("======write %d: %lu: counter=%d ++counter=%d\n", i, pthread_self(), t, ++counter); pthread_rwlock_unlock(&rwlock); usleep(5000); } return NULL; } void *th_read(void *arg) { int i = (int)arg; while(1){ pthread_rwlock_rdlock(&rwlock); printf("======read %d: %lu: %d\n", i, pthread_self(), counter); pthread_rwlock_unlock(&rwlock); usleep(900); } return NULL; } //3個線程不定時寫全局資源,5個線程不定時讀同一全局資源 int main() { int i; pthread_t tid[8]; //初始讀寫鎖 pthread_rwlock_init(&rwlock, NULL); for(i = 0; i < 3; i++) pthread_create(&tid[i], NULL, th_write, (void *)i); for(i = 0; i < 5; i++) pthread_create(&tid[i+3], NULL, th_read, (void *)i); for(i = 0; i < 8; i++) pthread_join(tid[i], NULL); //釋放讀寫鎖 pthread_rwlock_destroy(&rwlock); return 0; }
int pthread_cond_init(pthread_cond_t * restrict cond, const pthread_condattr_t * restrict attr);
pthread_cond_t cond = PTHREAD_COND_INITIALIZED;
int pthread_cond_destroy(pthread_cond_t *cond);
int pthread_cond_wait(pthread_cond_t * restrict cond, pthread_mutex_t * restrict mutex);
int pthread_cond_timedwait(pthread_cond_t * restrict cond, pthread_mutex_t * restrict mutex, const struct timespec * restrict abstime);
參3:參看man sem_timedwait
函數,查看struct timespec結構體。
struct timespec{ time_t tv_sec; /*seconds*/ 秒 long tv_nsec; /*nanoseconds*/ 納秒 };
如:time(NULL)返回的就是絕對時間。而alarm(1)是相對時間,相對當前時間定時1秒鐘。
struct timespec t = {1,0}; pthread_cond_timedwait(&cond, &mutex, &t); 只能定時到1970年1月1日 00:00:01秒(早已通過去)
在講解setitimer函數時咱們還提到另外一種時間類型
struct timeval{ time_t tv_sec; /*seconds*/ 秒 suseconds_t tv_usec; /*microseconds*/ 微秒 };
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);
看以下示例,使用條件變量模擬生產者、消費者問題:
/*藉助條件變量模擬,生產者-消費者問題*/ #include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <pthread.h> /*鏈表做爲共享數據,需被互斥量保護*/ struct msg { struct msg *next; int num; }; struct msg *head; struct msg *mp; /*靜態初始化一個條件變量和一個互斥量*/ pthread_cond_t has_product = PTHREAD_COND_INITIALIZER; pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; void *consumer(void *p) { for(;;){ pthread_mutex_lock(&lock); while(head == NULL){ //頭指針爲空,說明沒有節點 pthread_cond_wait(&has_product, &lock); } mp = head; head = mp->next; //模擬消費掉一個產品 pthread_mutex_unlock(&lock); printf("-Consume ---%d\n", mp->num); free(mp); sleep(rand() % 5); } } void *producer(void *p) { for(;;){ mp = malloc(sizeof(struct msg)); //模擬生產一個產品 mp->num = rand() % 1000 + 1; printf("-Produce ---%d\n", mp->num); pthread_mutex_lock(&lock); mp->next = head; head = mp; pthread_mutex_unlock(&lock); //將等待在該條件變量上的一個線程喚醒 pthread_cond_signal(&has_product); sleep(rand() % 5); } } int main(int argc, char * argv) { pthread_t pid, cid; srand(time(NULL)); pthread_create(&pid, NULL, producer, NULL); pthread_create(&cid, NULL, consumer, NULL); pthread_join(pid, NULL); pthread_join(cid, NULL); return 0; }
信號量,是相對摺中的一種處理方式,既能保證同步,數據不混亂,又能提升線程併發。
int sem_init(sem_t *sem, int pshared, unsigned int value);
int sem_destroy(sem_t *sem);
int sem_wait(sem_t *sem);
int sem_post(sem_t *sem);
int sem_trywait(sem_t *sem);
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
參2:abs_timeout採用的是絕對時間。
【練習】:使用信號量完成線程間同步,模擬生產者,消費者問題。
/*信號量實現生產者消費者問題*/ #include <stdio.h> #include <unistd.h> #include <pthread.h> #include <stdlib.h> #include <semaphore.h> #define NUM 5 int queue[NUM]; //全局數組實現環形隊列 sem_t blank_number, product_number; //空格子信號量,產品信號量 void *producer(void *arg) { int i = 0; while(1) { sem_wait(&blank_number); //生產者將空格子數--,爲0則阻塞等待 queue[i] = rand() % 1000 + 1; //生產一個產品 printf("----Produce----%d\n", queue[i]); sem_post(&product_number); //將產品數++ i = (i+1) % NUM; //藉助下標實現環形 sleep(rand() % 3); } return NULL; } void *consumer(void *arg) { int i = 0; while(1){ sem_wait(&product_number); //消費者將產品數--,爲0則阻塞等待 printf("--Consume---%d\n", queue[i]); queue[i] = 0; //消費一個產品 sem_post(&blank_number); //消費掉之後,將空格子數++ i = (i+1) % NUM; //藉助下標實現環形 sleep(rand() % 3); } return NULL; } int main() { pthread_t pid, cid; sem_init(&blank_number, 0, NUM); //初始化空格子信號量爲5 sem_init(&product_number, 0, 0); //產品數爲0 pthread_create(&pid, NULL, producer, NULL); pthread_create(&cid, NULL, consumer, NULL); pthread_join(pid, NULL); pthread_join(cid, NULL); sem_destroy(&blank_number); sem_destroy(&product_number); return 0; }
因此有:
T生產者主函數 { sem_wait(S空); 生產... sem_post(S滿) } T消費者主函數 { sem_wait(S滿); 消費... sem_post(S空) }
【做業】:結合生產者消費者信號量模型,揣摩sem_timedwait函數做用。編程實現,一個線程讀用戶輸入,另外一個線程打印「hello world」。若是用戶無輸入,則每隔5秒向屏幕打印一個「hello world」;若是用戶有輸入,馬上打印「hello world」到屏幕。
進程間也可使用互斥鎖,來達到同步的目的。但應在pthread_mutex_init初始化以前,修改其屬性爲進程間共享。mutex的屬性修改函數主要有如下幾個。
int pthread_mutexattr_init(pthread_mutexattr_t *attr);
int pthread_mutexattr_destroy(pthread_mutexattr_t *attr);
int pthread_mutexattr_setpshared(pthread_mutexattr_t *attr, int pshared);
進程間mutex示例
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <fcntl.h> #include <pthread.h> #include <sys/mman.h> #include <sys/wait.h> struct mt { int num; pthread_mutex_t mutex; pthread_mutexattr_t mutexattr; }; int main() { int i; struct mt *mm; pid_t pid; mm = mmap(NULL, sizeof(*mm), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANON, -1, 0); memset(mm, 0, sizeof(*mm)); pthread_mutexattr_init(&mm->mutexattr); //初始化mutex屬性對象 pthread_mutexattr_setpshared(&mm->mutexattr, PTHREAD_PROCESS_SHARED); //修改屬性爲進程間共享 pthread_mutex_init(&mm->mutex, &mm->mutexattr); //初始化一把mutex鎖 pid = fork(); if(pid == 0){ for(i = 0; i < 10; i++){ pthread_mutex_lock(&mm->mutex); (mm->num)++; printf("-Child------------num++ %d\n", mm->num); pthread_mutex_unlock(&mm->mutex); sleep(1); } } else if(pid > 0){ for(i = 0; i < 10; i++){ sleep(1); pthread_mutex_lock(&mm->mutex); mm->num+=2; printf("-------parent-----num+=2 %d\n", mm->num); pthread_mutex_unlock(&mm->mutex); } wait(NULL); } pthread_mutexattr_destroy(&mm->mutexattr); //銷燬mutex屬性對象 pthread_mutex_destroy(&mm->mutex); //銷燬mutex munmap(mm,sizeof(*mm)); //釋放映射區 return 0; }
參3:
struct flock { ... short l_type; /* 鎖的類型: F_RDLCK, F_WRLCK, F_UNLCK */ short l_whence; /* 偏移位置: SEEK_SET, SEEK_CUR, SEEK_END */ off_t l_start; /* 起始偏移:1000*/ off_t l_len; /* 長度:0表示整個文件加鎖 */ pid_t l_pid; /* 持有該鎖的進程ID:F_GETLK, F_OFD_GETLK */ ... };
進程間文件鎖示例
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <stdlib.h> void sys_err(char *str){ perror(str); exit(1); } int main(int argc, char *argv[]) { int fd; struct flock f_lock; if(argc < 2){ printf("./a.out filename\n"); exit(1); } if((fd = open(argv[1], O_RDWR)) < 0) sys_err("open"); f_lock.l_type = F_WRLCK; //選用寫鎖 //f_lock.l_type = F_RDLCK; //選用讀鎖 f_lock.l_whence = SEEK_SET; f_lock.l_start = 0; f_lock.l_len = 0; //0表示整個文件加鎖 fcntl(fd, F_SETLKW, &f_lock); printf("get flock\n"); sleep(10); f_lock.l_type = F_UNLCK; fcntl(fd, F_SETLKW, &f_lock); printf("un flock\n"); close(fd); return 0; }
void *tfn(void *arg)
,使用參數來表示線程編號:int i = (int)arg;
5支筷子,在邏輯上造成環,分別對應5個哲學家。
A B C D E 0 1 2 3 4
因此有:
if(i == 4) left = i, right = 0; else left = i, right = i + 1;
因此以上if else語句應改成
if(i == 4) left = 0, right = i; else left = i, right = i + 1;
然後,首先讓哲學家嘗試加左手鎖:
while(1){ pthread_mutex_lock(&m[left]); 若是加鎖成功,函數返回再加右手鎖,若是失敗,應當即釋放左手鎖,等待。 若左右手都加鎖成功 --> 吃 --> 吃完 --> 釋放鎖(應先釋放右手、再釋放左手,是加鎖順序的逆序) }
子進程中:
if(i == 4) left = 0, right = 4; else left = i, right = i + 1; while(1){ 使用sem_wait(&s[left])鎖左手,嘗試鎖右手,若成功 --> 吃;若不成功 --> 將左手鎖釋放。 吃完後,先釋放右手鎖,再釋放左手鎖。 }