系統與網絡編程
小作業
- 公交車停發車程序
#include <stdio.h>
#include <string.h>
#include <unistd.h> //fork ()
#include <stdlib.h>
#include <sys/types.h> //kill()
#include <signal.h> //signal()
pid_t pid = -1;
//司機:到站停車 --> SIGINT ctrl+c
// 關門開車 --> SIGUSR1
void handleDriver(int sig)
{
if (SIGINT == sig)
{
printf("到站了...\n");
sleep(1);
printf("司機開始停車...\n");
sleep(1);
int ret = 0;
ret = kill(pid, SIGUSR2); //發送信號給售票員讓她開門
if (-1 == ret)
{
perror("kill");
}
}
else if (SIGUSR1 == sig)
{
printf("司機開始開車...\n");
sleep(1);
printf("車正在跑...\n");
sleep(1);
}
}
//售票員:開門 -->SIGUSR2
void handleConductor(int sig)
{
if (SIGUSR2 == sig)
{
printf("售票員開門...\n");
sleep(1);
printf("乘客上車...\n");
sleep(1);
printf("售票員關門...\n");
sleep(1);
kill(getppid(), SIGUSR1); //售票員發送信號給司機開車
}
}
int main(void)
{
pid = fork();
if (pid > 0) //driver
{
signal(SIGINT, handleDriver);
signal(SIGUSR1, handleDriver);
printf("司機等待售票員做好開車準備...\n");
while (1)
{
pause();
}
}
else if (0 == pid) //conductor
{
signal(SIGINT, SIG_IGN);
signal(
SIGUSR2, handleConductor);
sleep(1);
//發送一個開車信號讓其開車
kill(getppid(), SIGUSR1);
while (1)
{
pause();
}
}
else if (-1 == pid)
{
perror("fork");
return -1;
}
return 0;
}
Paste_Image.png
線程
并發執行:看起來像同時運行,實際上在單核cpu里只有一個。將其排成進程列表,每個進程分配執行時間,執行完后掛起,下個進程開始進行。
-
創建線程:
- int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void (start_routine) (void *), void *arg); - 第一個參數:線程的標識,第二個參數:線程的屬性,第三個參數:指定線程執行的代碼,第四個參數:傳給這個線程的實參。
- 編譯的時候要加上-pthread。
#include <pthread.h> #include <stdio.h> #include <string.h> #include <errno.h> #include <unistd.h> void *thread_run(void *arg) { while(1) { printf("this is thread_run...\n"); sleep(1); } return NULL; } int main() { pthread_t thread;//線程的標識 int ret=0; ret=pthread_create(&thread,NULL,thread_run,NULL); if(ret!=0) { printf("errno:%d,error:%s\n",ret,strerror(ret)); return -1; } while(1) { printf("this is main...\n"); sleep(1); } return 0; }
Paste_Image.png - int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
-
線程特點
- 一個進程創建后,創建的線程從屬于該進程,不獨立于進程,共享進程的所有資源
- 線程是最小的執行單元
- 若一個進程創建線程后,我們可以將該進程稱之為主線程
- 一個進程可以創建多個線程,線程之間資源共享
- 若一個進程沒有創建線程既可以把它看作是進程,也可以把它看做是線程
-
使用return只能結束當前線程
Paste_Image.png -
exit(1)會將線程所屬的進程掛掉,當然線程也會掛掉
Paste_Image.png
-
線程資源共享
- 電腦會分配給每部分一段運行時間,當運行時間到了以后就會切換到下一個進程
#include <pthread.h> #include <stdio.h> #include <string.h> #include <errno.h> #include <unistd.h> int g_data=0; void *thread_run(void *arg) { while(1) { g_data++; printf("this is thread_run...%d\n",g_data); // sleep(1); } return NULL; } int main() { pthread_t thread;//線程的標識 int ret=0; ret=pthread_create(&thread,NULL,thread_run,NULL); if(ret!=0) { printf("errno:%d,error:%s\n",ret,strerror(ret)); return -1; } while(1) { g_data++; printf("this is main%d\n",g_data); // sleep(1); } return 0; }
Paste_Image.png- 解決方法:定義一個鎖
#include <pthread.h> #include <stdio.h> #include <string.h> #include <errno.h> #include <unistd.h> pthread_mutex_t mutex; //互斥量,鎖 int g_data=0; void *thread_run(void *arg) { while(1) { pthread_mutex_lock(&mutex); g_data++; printf("this is thread_run...%d\n",g_data); pthread_mutex_unlock(&mutex); // sleep(1); } return NULL; } int main() { //初始化互斥量 pthread_mutex_init(&mutex,NULL);//NULL:使用默認屬性 pthread_t thread;//線程的標識 int ret=0; ret=pthread_create(&thread,NULL,thread_run,NULL); if(ret!=0) { printf("errno:%d,error:%s\n",ret,strerror(ret)); return -1; } while(1) { g_data++; printf("this is main%d\n",g_data); // sleep(1); } return 0; }
Paste_Image.png- 實現文件讀與打印的線程
#include <stdio.h> #include <unistd.h> //write() read() #include <errno.h> //errno #include <string.h> //strerror() /*open()*/ #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <pthread.h> //pthread_create pthread_mutex_t mutex; #define NAME_LEN 32 typedef struct Student { int iId; char caName[NAME_LEN]; char cSex; float fScore; }Student; int myOpen(const char *pathname) { int fd = -1; if (NULL != pathname) { fd = open(pathname, O_RDONLY | O_CREAT , S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); if (-1 == fd) { printf("open error: %s\n", strerror(errno)); } } return fd; } int g_iSign = 0; void *read_thread(void *arg) { int fd = -1; fd = myOpen("stu.info"); if (-1 != fd) { int ret = -1; Student *pStu = (Student *)arg; while (1) { if (0 == g_iSign) { pthread_mutex_lock(&mutex); memset(pStu, '\0', sizeof(Student)); ret = read(fd, pStu, sizeof(Student)); if (0 == ret) { printf("reached the file end\n"); pthread_mutex_unlock(&mutex); g_iSign = 1; break; } else if (-1 == ret) { printf("read error:%s\n", strerror(errno)); pthread_mutex_unlock(&mutex); g_iSign = 1; break; } pthread_mutex_unlock(&mutex); g_iSign = 1; } } close(fd); } return NULL; } void *print_thread(void *arg) { Student *pStu = (Student *)arg; int i = 0; while (1) { if (1 == g_iSign) { pthread_mutex_lock(&mutex); if (0 == pStu->iId) { pthread_mutex_unlock(&mutex); break; } printf("id:%d, name:%s, sex:%c, score:%.1f\n" , pStu->iId, pStu->caName , pStu->cSex, pStu->fScore); pthread_mutex_unlock(&mutex); g_iSign = 0; } } return NULL; } int main(void) { pthread_mutex_init(&mutex, NULL); Student stu; pthread_t pthr_read; pthread_t pthr_show; pthread_create(&pthr_read, NULL, read_thread, &stu); pthread_create(&pthr_show, NULL, print_thread, &stu); pthread_join(pthr_read, NULL); pthread_join(pthr_show, NULL); return 0; }
-
線程結束
- 發送一個結束線程請求給指定的線,默認情況下線程是會同意該結束線程請求,還可以設置不同意
- 在線程函數中調用pthread_setcancelstate來設置,不同意結束線程結束請求,阻塞線程結束請求,直到線程允許接收線程結束請求
#include <pthread.h> #include <stdio.h> #include <string.h> #include <errno.h> #include <unistd.h> void *thread_run(void *arg) { //在線程函數中調用pthread_setcancelstate來設置 //不同意結束線程結束請求,阻塞線程結束請求 //直到線程允許接收線程結束請求 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL); while(1) { printf("this is thread_run...\n"); sleep(1); } return NULL; } int main() { pthread_t thread;//線程的標識 int ret=0; ret=pthread_create(&thread,NULL,thread_run,NULL); if(ret!=0) { printf("errno:%d,error:%s\n",ret,strerror(ret)); return -1; } sleep(3); //發送一個結束線程請求給指定的線程 //默認情況下線程是會同意該結束線程請求 //還可以設置不同意 pthread_cancel(thread);//掛起線程 while(1) { printf("this is main\n"); sleep(1); } return 0; }
Paste_Image.png
Paste_Image.png- pthread_exit(NULL);
#include <pthread.h> #include <stdio.h> #include <string.h> #include <errno.h> #include <unistd.h> void *thread_run(void *arg) { pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL); while(1) { printf("this is thread_run...\n"); sleep(1); break; pthread_exit(NULL);//結束所屬線程 } return NULL; } int main() { pthread_t thread;//線程的標識 int ret=0; ret=pthread_create(&thread,NULL,thread_run,NULL); if(ret!=0) { printf("errno:%d,error:%s\n",ret,strerror(ret)); return -1; } sleep(3); pthread_exit(NULL);//等待其他線程結束,此過程會阻塞,隨后結束主函數。 while(1) { printf("this is main\n"); sleep(1); } return 0; }
- pthread_join(thread,NULL);//若線程在運行,則阻塞等待,若不在運行,則立即返回
#include <pthread.h> #include <stdio.h> #include <string.h> #include <errno.h> #include <unistd.h> void *thread_run(void *arg) { pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL); while(1) { printf("this is thread_run...\n"); sleep(1); } return NULL; } int main() { pthread_t thread;//線程的標識 int ret=0; ret=pthread_create(&thread,NULL,thread_run,NULL); if(ret!=0) { printf("errno:%d,error:%s\n",ret,strerror(ret)); return -1; } pthread_join(thread,NULL); while(1) { printf("this is main\n"); sleep(1); } return 0; }
-
信號量:
- int sem_init(sem_t *sem, int pshared, unsigned int value);
- 第一個是信號量,第二個是初始線程還是進程,第三個值表示滿足不滿足
#include <stdio.h> #include <unistd.h> //write() read() #include <errno.h> //errno #include <string.h> //strerror() /*open()*/ #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <pthread.h> //pthread_create #include <semaphore.h> pthread_mutex_t mutex; sem_t read_sem; //整形 sem_t show_sem; #define NAME_LEN 32 typedef struct Student { int iId; char caName[NAME_LEN]; char cSex; float fScore; }Student; int myOpen(const char *pathname) { int fd = -1; if (NULL != pathname) { fd = open(pathname, O_RDONLY | O_CREAT , S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); if (-1 == fd) { printf("open error: %s\n", strerror(errno)); } } return fd; } void *read_thread(void *arg) { int fd = -1; fd = myOpen("stu.info"); if (-1 != fd) { int ret = -1; Student *pStu = (Student *)arg; while (1) { //如果read_sem大于0,接著往下執行,并且將該變量減一 //如果read_sem等于0,則阻塞,直到該值大于0 sem_wait(&read_sem); pthread_mutex_lock(&mutex); memset(pStu, '\0', sizeof(Student)); ret = read(fd, pStu, sizeof(Student)); if (0 == ret) { printf("reached the file end\n"); pthread_mutex_unlock(&mutex); sem_post(&show_sem); break; } else if (-1 == ret) { printf("read error:%s\n", strerror(errno)); pthread_mutex_unlock(&mutex); sem_post(&show_sem); break; } pthread_mutex_unlock(&mutex); //將信號量的值加一 sem_post(&show_sem); } } close(fd); return NULL; } void *print_thread(void *arg) { Student *pStu = (Student *)arg; int i = 0; while (1) { { //如果show_sem大于0,接著往下執行,并且將該變量減一 //如果show_sem等于0,則阻塞,直到該值大于0 sem_wait(&show_sem); pthread_mutex_lock(&mutex); if (0 == pStu->iId) { pthread_mutex_unlock(&mutex); sem_post(&read_sem); break; } printf("id:%d, name:%s, sex:%c, score:%.1f\n" , pStu->iId, pStu->caName , pStu->cSex, pStu->fScore); pthread_mutex_unlock(&mutex); //將信號量的值加一 sem_post(&read_sem); } } return NULL; } int main(void) { pthread_mutex_init(&mutex, NULL); //初始化信號量 sem_init(&read_sem, 0, 1); //將read_sem值置為1 sem_init(&show_sem, 0, 0); //將show_sem值置為0 Student stu; pthread_t pthr_read; pthread_t pthr_show; pthread_create(&pthr_read, NULL, read_thread, &stu); pthread_create(&pthr_show, NULL, print_thread, &stu); pthread_join(pthr_read, NULL); pthread_join(pthr_show, NULL); return 0; }
pthread_addNum
#include<pthread.h> //pthread_create()
#include<stdio.h>
#include<string.h>//strerror()
#include<errno.h> //errno
#include<stdlib.h>
int g_iData=0;
void *thread_run(void *arg)
{
while(1)
{
++g_iData;
printf("thread :data=%d\n",g_iData);
}
return NULL;
}
int main()
{
pthread_t thread;
int ret=0;
ret=pthread_create(&thread,NULL,thread_run,NULL);//現在線程的屬性默認為NULL
if(0!=ret)
{
printf("errno:%d,error:%s\n",ret,strerror(ret));
return -1;
}
while(1)
{
++g_iData;
printf("main :data=%d\n",g_iData);
}
return 0;
}
- g_iData=0;A線程++g_iData,如果線程A時間足夠的話,則g_iData=1;B線程再執行,則變為2;
- 若A時間片不夠,雖然++了但++后的值還沒有返回到g_iData里,所以B線程++后的值仍然為1
如下,會出現中斷的情況
Paste_Image.png
- 解決以上情況,則定義一個原子鎖
#include<pthread.h> //pthread_create()
#include<stdio.h>
#include<string.h>//strerror()
#include<errno.h> //errno
#include<stdlib.h>
pthread_mutex_t mutex; //互斥量或者稱為鎖
int g_iData=0;
void *thread_run(void *arg)
{
while(1)
{
//使用pthread_mutex_lock和pthread_mutex_unlock
//使它們之間的語句合成原子操作
pthread_mutex_lock(&mutex);
++g_iData;
printf("thread :data=%d\n",g_iData);
pthread_mutex_unlock(&mutex);
}
return NULL;
}
int main()
{
//初始化互斥量,NULL表示使用默認屬性初始化該互斥量
pthread_mutex_init(&mutex,NULL);//使用他的默認屬性進行初始化
pthread_t thread;
int ret=0;
ret=pthread_create(&thread,NULL,thread_run,NULL);//現在線程的屬性默認為NULL
if(0!=ret)
{
printf("errno:%d,error:%s\n",ret,strerror(ret));
return -1;
}
while(1)
{
pthread_mutex_lock(&mutex);
++g_iData;
printf("main :data=%d\n",g_iData);
pthread_mutex_unlock(&mutex);
}
return 0;
}
- 這樣就不會出現中斷的情況
一個線程從文件讀取數據,另一個線程打印出來
- 先通過以前的代碼,在文件里寫入幾個同學的信息
#include <stdio.h>
#include <unistd.h> //write() read()
#include <errno.h> //errno
#include <string.h> //strerror()
/*open()*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h> //pthread_create
pthread_mutex_t mutex;
#define NAME_LEN 32
typedef struct Student
{
int iId;
char caName[NAME_LEN];
char cSex;
float fScore;
}Student;
int myOpen(const char *pathname)
{
int fd = -1;
if (NULL != pathname)
{
fd = open(pathname, O_RDONLY | O_CREAT
, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
if (-1 == fd)
{
printf("open error: %s\n", strerror(errno));
}
}
return fd;
}
int g_iSign = 0;
void *read_thread(void *arg)
{
int fd = -1;
fd = myOpen("stu.info");
if (-1 != fd)
{
int ret = -1;
Student *pStu = (Student *)arg;
while (1)
{
if (0 == g_iSign)
{
pthread_mutex_lock(&mutex);
memset(pStu, '\0', sizeof(Student));
ret = read(fd, pStu, sizeof(Student));
if (0 == ret)
{
printf("reached the file end\n");
pthread_mutex_unlock(&mutex);
g_iSign = 1;
break;
}
else if (-1 == ret)
{
printf("read error:%s\n", strerror(errno));
pthread_mutex_unlock(&mutex);
g_iSign = 1;
break;
}
pthread_mutex_unlock(&mutex);
g_iSign = 1;
}
}
close(fd);
}
return NULL;
}
void *print_thread(void *arg)
{
Student *pStu = (Student *)arg;
int i = 0;
while (1)
{
if (1 == g_iSign)
{
pthread_mutex_lock(&mutex);
if (0 == pStu->iId)
{
pthread_mutex_unlock(&mutex);
break;
}
printf("id:%d, name:%s, sex:%c, score:%.1f\n"
, pStu->iId, pStu->caName
, pStu->cSex, pStu->fScore);
pthread_mutex_unlock(&mutex);
g_iSign = 0;
}
}
return NULL;
}
int main(void)
{
pthread_mutex_init(&mutex, NULL);
Student stu;
pthread_t pthr_read;
pthread_t pthr_show;
pthread_create(&pthr_read, NULL, read_thread, &stu);
pthread_create(&pthr_show, NULL, print_thread, &stu);
pthread_join(pthr_read, NULL);
pthread_join(pthr_show, NULL);
return 0;
}
Paste_Image.png
信號量(sem_wait)
//int sem_init();//pshared
#include <stdio.h>
#include <unistd.h> //write() read()
#include <errno.h> //errno
#include <string.h> //strerror()
/*open()*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h> //pthread_create
#include <semaphore.h>
pthread_mutex_t mutex;
sem_t read_sem;//讀的信號量
sem_t print_sem;//顯示的信號量
#define NAME_LEN 32
typedef struct Student
{
int iId;
char caName[NAME_LEN];
char cSex;
float fScore;
}Student;
int myOpen(const char *pathname)
{
int fd = -1;
if (NULL != pathname)
{
fd = open(pathname, O_RDONLY | O_CREAT
, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
if (-1 == fd)
{
printf("open error: %s\n", strerror(errno));
}
}
return fd;
}
void *read_thread(void *arg)
{
int fd = -1;
fd = myOpen("stu.info");
if (-1 != fd)
{
int ret = -1;
Student *pStu = (Student *)arg;
while (1)
{
//如果read_sem大于0,接著往下執行,并且將該變量減1,如果等于0,則阻塞,直到該值大于0
sem_wait(&read_sem);//此時read_sem=1
pthread_mutex_lock(&mutex);
memset(pStu, '\0', sizeof(Student));
ret = read(fd, pStu, sizeof(Student));
if (0 == ret)
{
printf("reached the file end\n");
pthread_mutex_unlock(&mutex);
sem_post(&print_sem);//出錯了,發送這個信號
break;
}
else if (-1 == ret)
{
printf("read error:%s\n", strerror(errno));
pthread_mutex_unlock(&mutex);
sem_post(&print_sem);//出錯了,發送這個信號
break;
}
pthread_mutex_unlock(&mutex);
sem_post(&print_sem);//出錯了,發送這個信號
}
close(fd);
}
return NULL;
}
void *print_thread(void *arg)
{
Student *pStu = (Student *)arg;
int i = 0;
while (1)
{
//如果print_sem大于0,接著往下執行,并且將該變量減1,如果等于0,則阻塞,直到該值大于0
sem_wait(&print_sem);
pthread_mutex_lock(&mutex);
if (0 == pStu->iId)
{
pthread_mutex_unlock(&mutex);
sem_post(&read_sem);
break;
}
printf("id:%d, name:%s, sex:%c, score:%.1f\n"
, pStu->iId, pStu->caName
, pStu->cSex, pStu->fScore);
pthread_mutex_unlock(&mutex);
//將信號量的值加1
sem_post(&read_sem);
}
return NULL;
}
int main(void)
{
pthread_mutex_init(&mutex, NULL);
sem_init(&read_sem,0,1);//對信號量的初始化,必須先讀才能再顯示,將read_sem的值置為1
sem_init(&print_sem,0,0);//將print_sem的值置為0
Student stu;
pthread_t pthr_read;
pthread_t pthr_show;
pthread_create(&pthr_read, NULL, read_thread, &stu);
pthread_create(&pthr_show, NULL, print_thread, &stu);
pthread_join(pthr_read, NULL);
pthread_join(pthr_show, NULL);
return 0;
}
Paste_Image.png
用代碼模仿5個哲學家進餐問題
- 創建5根筷子(5個信號量創建一組信號量)
- 創建5科學家
- 獲得筷子
- 放下筷子
- 繼續思考
int semop(int semid, struct sembuf *sops, unsigned nsops);
參數
- semid:信號集的識別碼,可通過semget獲取。
- sops:指向存儲信號操作結構的數組指針,信號操作結構的原型如下
struct sembuf
{
unsigned short sem_num; /* semaphore number */
short sem_op; /* semaphore operation */
short sem_flg; /* operation flags */
};
- 這三個字段的意義分別為:
- sem_num:操作信號在信號集中的編號,第一個信號的編號是0。
- sem_op:
- 1, 如果其值為正數,該值會加到現有的信號內含值中。
通常用于釋放所控資源的使用權; - 2, 如果sem_op的值為負數,而其絕對值又大于信號的現值,
操作將會阻塞,直到信號值大于或等于sem_op的絕對值。
通常用于獲取資源的使用權; - 3, 如果sem_op的值為0,如果沒有設置IPC_NOWAIT,
則調用該操作的進程或者線程將暫時睡眠,直到信號量的值為0; 否則,進程或者線程不會睡眠,函數返回錯誤EAGAIN。
- sem_flg:信號操作標志,可能的選擇有兩種
- 1, IPC_NOWAIT //對信號的操作不能滿足時,semop()不會阻塞,
并立即返回,同時設定錯誤信息。 - 2, SEM_UNDO //程序結束時(不論正?;虿徽?,
保證信號值會被重設為semop()調用前的值。
這樣做的目的在于避免程序在異常情況下結束時
未將鎖定的資源解鎖,造成該資源永遠鎖定。
- nsops:信號操作結構的數量,恒大于或等于1。
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <string.h>//perror()
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <unistd.h>
#include <stdio.h>
union semun
{
int val;
struct semid_ds *buf;
unsigned short *array;
struct seminfo *__buf;
};
void getChopsticks(int iNum,int *semfd)
{
int iLeft=iNum;
int iRight=(iNum+1)%5;
struct sembuf semope[2]={{iLeft,-1,0},
{iRight,-1,0}};
semop(*semfd,semope,2);//拿筷子,獲得左右兩邊的筷子
}
void putChopsticks(int iNum,int *semfd)
{
int iLeft=iNum;
int iRight=(iNum+1)%5;
struct sembuf semope[2]={{iLeft,1,0},
{iRight,1,0}};
semop(*semfd,semope,2);//拿筷子,獲得左右兩邊的筷子
}
void thinkAndEat(int iNum,int *semfd)
{
while(1)
{
printf("%d say:I am thinking...\n",iNum);
/*拿筷子吃飯*/
getChopsticks(iNum,semfd);
sleep(1);
printf("%d say:I am eatting...\n",iNum);
/*放下筷子*/
putChopsticks(iNum,semfd);
printf("%d say:I am puting chopsticks...\n",iNum);
sleep(1);
}
}
int main(void)
{
int semfd=-1;
//獲得信號量集的標識,若信號量集不存在則創建
semfd=semget(0x1024,5,IPC_CREAT | 0777);//5,代表5個信號量
if(-1==semfd)
{
perror("semget");
return -1;
}
//對信號集中的信號量進行賦值
union semun sem;
sem.val=1;
int i=0;
for(;i<5;i++)
{
if(-1==semctl(semfd,i,SETVAL,sem))
{
perror("semctl");
return -1;
}
}
//創建5個哲學家進程
int iNum=0;//用來保存表示第幾個科學家
pid_t pid=-1;
for(i=0;i<4;i++)
{
pid=fork();
if(pid>0)//parent
{
iNum=4;
}
else if(pid==0)//child
{
iNum=i;
break;//
}
else if(pid==-1)//error
{
return -1;
}
}
thinkAndEat(iNum,&semfd);
return 0;
}
Paste_Image.png
- 生產者消費者問題
Paste_Image.png
link