Linux多线程开发(三)
Linux多线程开发(三)
线程概述
线程概述
线程和进程区别
线程之间共享和非共享资源
NPTL
线程创建
代码
1 /* 2 一般情况下,main函数所在的线程我们称之为主线程(main线程),其余创建的线程 3 称之为子线程。 4 程序中默认只有一个进程,fork()函数调用,2进行 5 程序中默认只有一个线程,pthread_create()函数调用,2个线程。 6 7 #include <pthread.h> 8 int pthread_create(pthread_t *thread, const pthread_attr_t *attr, 9 void *(*start_routine) (void *), void *arg); 10 11 - 功能:创建一个子线程 12 - 参数: 13 - thread:传出参数,线程创建成功后,子线程的线程ID被写到该变量中。 14 - attr : 设置线程的属性,一般使用默认值,NULL 15 - start_routine : 函数指针,这个函数是子线程需要处理的逻辑代码 16 - arg : 给第三个参数使用,传参 17 - 返回值: 18 成功:0 19 失败:返回错误号。这个错误号和之前errno不太一样。 20 获取错误号的信息: char * strerror(int errnum); 21 22 */ 23 #include <stdio.h> 24 #include <pthread.h> 25 #include <string.h> 26 #include <unistd.h> 27 28 void * callback(void * arg) { 29 printf("child thread...\n"); 30 printf("arg value: %d\n", *(int *)arg); 31 return NULL; 32 } 33 34 int main() { 35 36 pthread_t tid; 37 38 int num = 10; 39 40 // 创建一个子线程 41 int ret = pthread_create(&tid, NULL, callback, (void *)&num); 42 43 if(ret != 0) { 44 char * errstr = strerror(ret); 45 printf("error : %s\n", errstr); 46 } 47 48 for(int i = 0; i < 5; i++) { 49 printf("%d\n", i); 50 } 51 52 sleep(1); 53 54 return 0; // exit(0); 55 }
终止线程
代码
1 /* 2 3 #include <pthread.h> 4 void pthread_exit(void *retval); 5 功能:终止一个线程,在哪个线程中调用,就表示终止哪个线程 6 参数: 7 retval:需要传递一个指针,作为一个返回值,可以在pthread_join()中获取到。 8 9 pthread_t pthread_self(void); 10 功能:获取当前的线程的线程ID 11 12 int pthread_equal(pthread_t t1, pthread_t t2); 13 功能:比较两个线程ID是否相等 14 不同的操作系统,pthread_t类型的实现不一样,有的是无符号的长整型,有的 15 是使用结构体去实现的。 16 */ 17 #include <stdio.h> 18 #include <pthread.h> 19 #include <string.h> 20 21 void * callback(void * arg) { 22 printf("child thread id : %ld\n", pthread_self()); 23 return NULL; // pthread_exit(NULL); 24 } 25 26 int main() { 27 28 // 创建一个子线程 29 pthread_t tid; 30 int ret = pthread_create(&tid, NULL, callback, NULL); 31 32 if(ret != 0) { 33 char * errstr = strerror(ret); 34 printf("error : %s\n", errstr); 35 } 36 37 // 主线程 38 for(int i = 0; i < 5; i++) { 39 printf("%d\n", i); 40 } 41 42 printf("tid : %ld, main thread id : %ld\n", tid ,pthread_self()); 43 44 // 让主线程退出,当主线程退出时,不会影响其他正常运行的线程。 45 pthread_exit(NULL); 46 47 printf("main thread exit\n"); 48 49 return 0; // exit(0); 50 }
pthread_self当前进程的id
连接已终止的线程
代码
1 /* 2 #include <pthread.h> 3 int pthread_join(pthread_t thread, void **retval); 4 - 功能:和一个已经终止的线程进行连接 5 回收子线程的资源 6 这个函数是阻塞函数,调用一次只能回收一个子线程 7 一般在主线程中使用 8 - 参数: 9 - thread:需要回收的子线程的ID 10 - retval: 接收子线程退出时的返回值 11 - 返回值: 12 0 : 成功 13 非0 : 失败,返回的错误号 14 */ 15 16 #include <stdio.h> 17 #include <pthread.h> 18 #include <string.h> 19 #include <unistd.h> 20 21 int value = 10; 22 23 void * callback(void * arg) { 24 printf("child thread id : %ld\n", pthread_self()); 25 // sleep(3); 26 // return NULL; 27 // int value = 10; // 局部变量 28 pthread_exit((void *)&value); // return (void *)&value; 29 } 30 31 int main() { 32 33 // 创建一个子线程 34 pthread_t tid; 35 int ret = pthread_create(&tid, NULL, callback, NULL); 36 37 if(ret != 0) { 38 char * errstr = strerror(ret); 39 printf("error : %s\n", errstr); 40 } 41 42 // 主线程 43 for(int i = 0; i < 5; i++) { 44 printf("%d\n", i); 45 } 46 47 printf("tid : %ld, main thread id : %ld\n", tid ,pthread_self()); 48 49 // 主线程调用pthread_join()回收子线程的资源 50 int * thread_retval; 51 ret = pthread_join(tid, (void **)&thread_retval); 52 53 if(ret != 0) { 54 char * errstr = strerror(ret); 55 printf("error : %s\n", errstr); 56 } 57 58 printf("exit data : %d\n", *thread_retval); 59 60 printf("回收子线程资源成功!\n"); 61 62 // 让主线程退出,当主线程退出时,不会影响其他正常运行的线程。 63 pthread_exit(NULL); 64 65 return 0; 66 }
线程的分离
代码
1 /* 2 #include <pthread.h> 3 int pthread_detach(pthread_t thread); 4 - 功能:分离一个线程。被分离的线程在终止的时候,会自动释放资源返回给系统。 5 1.不能多次分离,会产生不可预料的行为。 6 2.不能去连接一个已经分离的线程,会报错。 7 - 参数:需要分离的线程的ID 8 - 返回值: 9 成功:0 10 失败:返回错误号 11 */ 12 #include <stdio.h> 13 #include <pthread.h> 14 #include <string.h> 15 #include <unistd.h> 16 17 void * callback(void * arg) { 18 printf("chid thread id : %ld\n", pthread_self()); 19 return NULL; 20 } 21 22 int main() { 23 24 // 创建一个子线程 25 pthread_t tid; 26 27 int ret = pthread_create(&tid, NULL, callback, NULL); 28 if(ret != 0) { 29 char * errstr = strerror(ret); 30 printf("error1 : %s\n", errstr); 31 } 32 33 // 输出主线程和子线程的id 34 printf("tid : %ld, main thread id : %ld\n", tid, pthread_self()); 35 36 // 设置子线程分离,子线程分离后,子线程结束时对应的资源就不需要主线程释放 37 ret = pthread_detach(tid); 38 if(ret != 0) { 39 char * errstr = strerror(ret); 40 printf("error2 : %s\n", errstr); 41 } 42 43 // 设置分离后,对分离的子线程进行连接 pthread_join() 44 // ret = pthread_join(tid, NULL); 45 // if(ret != 0) { 46 // char * errstr = strerror(ret); 47 // printf("error3 : %s\n", errstr); 48 // } 49 50 pthread_exit(NULL); 51 52 return 0; 53 }
线程操作
线程取消
pthread_cancel:取消线程或者是让线程终止
是遇到终止点才取消,没遇到终止点不取消。
代码
1 /* 2 #include <pthread.h> 3 int pthread_cancel(pthread_t thread); 4 - 功能:取消线程(让线程终止) 5 取消某个线程,可以终止某个线程的运行, 6 但是并不是立马终止,而是当子线程执行到一个取消点,线程才会终止。 7 取消点:系统规定好的一些系统调用,我们可以粗略的理解为从用户区到内核区的切换,这个位置称之为取消点。 8 */ 9 10 #include <stdio.h> 11 #include <pthread.h> 12 #include <string.h> 13 #include <unistd.h> 14 15 void * callback(void * arg) { 16 printf("chid thread id : %ld\n", pthread_self()); 17 for(int i = 0; i < 5; i++) { 18 printf("child : %d\n", i); 19 } 20 return NULL; 21 } 22 23 int main() { 24 25 // 创建一个子线程 26 pthread_t tid; 27 28 int ret = pthread_create(&tid, NULL, callback, NULL); 29 if(ret != 0) { 30 char * errstr = strerror(ret); 31 printf("error1 : %s\n", errstr); 32 } 33 34 // 取消线程 35 pthread_cancel(tid); 36 37 for(int i = 0; i < 5; i++) { 38 printf("%d\n", i); 39 } 40 41 // 输出主线程和子线程的id 42 printf("tid : %ld, main thread id : %ld\n", tid, pthread_self()); 43 44 45 pthread_exit(NULL); 46 47 return 0; 48 }
线程属性
代码
1 /* 2 int pthread_attr_init(pthread_attr_t *attr); 3 - 初始化线程属性变量 4 5 int pthread_attr_destroy(pthread_attr_t *attr); 6 - 释放线程属性的资源 7 8 int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate); 9 - 获取线程分离的状态属性 10 11 int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate); 12 - 设置线程分离的状态属性 13 */ 14 15 #include <stdio.h> 16 #include <pthread.h> 17 #include <string.h> 18 #include <unistd.h> 19 20 void * callback(void * arg) { 21 printf("chid thread id : %ld\n", pthread_self()); 22 return NULL; 23 } 24 25 int main() { 26 27 // 创建一个线程属性变量 28 pthread_attr_t attr; 29 // 初始化属性变量 30 pthread_attr_init(&attr); 31 32 // 设置属性 33 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 34 35 // 创建一个子线程 36 pthread_t tid; 37 38 int ret = pthread_create(&tid, &attr, callback, NULL); 39 if(ret != 0) { 40 char * errstr = strerror(ret); 41 printf("error1 : %s\n", errstr); 42 } 43 44 // 获取线程的栈的大小 45 size_t size; 46 pthread_attr_getstacksize(&attr, &size); 47 printf("thread stack size : %ld\n", size); 48 49 // 输出主线程和子线程的id 50 printf("tid : %ld, main thread id : %ld\n", tid, pthread_self()); 51 52 // 释放线程属性资源 53 pthread_attr_destroy(&attr); 54 55 pthread_exit(NULL); 56 57 return 0; 58 }
线程同步
代码
1 /* 2 使用多线程实现买票的案例。 3 有3个窗口,一共是100张票。 4 */ 5 6 #include <stdio.h> 7 #include <pthread.h> 8 #include <unistd.h> 9 10 // 全局变量,所有的线程都共享这一份资源。 11 int tickets = 100; 12 13 void * sellticket(void * arg) { 14 // 卖票 15 while(tickets > 0) { 16 usleep(6000); 17 printf("%ld 正在卖第 %d 张门票\n", pthread_self(), tickets); 18 tickets--; 19 } 20 return NULL; 21 } 22 23 int main() { 24 25 // 创建3个子线程 26 pthread_t tid1, tid2, tid3; 27 pthread_create(&tid1, NULL, sellticket, NULL); 28 pthread_create(&tid2, NULL, sellticket, NULL); 29 pthread_create(&tid3, NULL, sellticket, NULL); 30 31 // 回收子线程的资源,阻塞 32 pthread_join(tid1, NULL); 33 pthread_join(tid2, NULL); 34 pthread_join(tid3, NULL); 35 36 // 设置线程分离。 37 // pthread_detach(tid1); 38 // pthread_detach(tid2); 39 // pthread_detach(tid3); 40 41 pthread_exit(NULL); // 退出主线程 42 43 return 0; 44 }
注意:分离后join,会报错。
usleep(3000):3000微秒。
互斥锁
互斥量
代码
1 /* 2 互斥量的类型 pthread_mutex_t 3 int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr); 4 - 初始化互斥量 5 - 参数 : 6 - mutex : 需要初始化的互斥量变量 7 - attr : 互斥量相关的属性,NULL 8 - restrict : C语言的修饰符,被修饰的指针,不能由另外的一个指针进行操作。 9 pthread_mutex_t *restrict mutex = xxx; 10 pthread_mutex_t * mutex1 = mutex; 11 12 int pthread_mutex_destroy(pthread_mutex_t *mutex); 13 - 释放互斥量的资源 14 15 int pthread_mutex_lock(pthread_mutex_t *mutex); 16 - 加锁,阻塞的,如果有一个线程加锁了,那么其他的线程只能阻塞等待 17 18 int pthread_mutex_trylock(pthread_mutex_t *mutex); 19 - 尝试加锁,如果加锁失败,不会阻塞,会直接返回。 20 21 int pthread_mutex_unlock(pthread_mutex_t *mutex); 22 - 解锁 23 */ 24 #include <stdio.h> 25 #include <pthread.h> 26 #include <unistd.h> 27 28 // 全局变量,所有的线程都共享这一份资源。 29 int tickets = 1000; 30 31 // 创建一个互斥量 32 pthread_mutex_t mutex; 33 34 void * sellticket(void * arg) { 35 36 // 卖票 37 while(1) { 38 39 // 加锁 40 pthread_mutex_lock(&mutex); 41 42 if(tickets > 0) { 43 usleep(6000); 44 printf("%ld 正在卖第 %d 张门票\n", pthread_self(), tickets); 45 tickets--; 46 }else { 47 // 解锁 48 pthread_mutex_unlock(&mutex); 49 break; 50 } 51 52 // 解锁 53 pthread_mutex_unlock(&mutex); 54 } 55 56 57 58 return NULL; 59 } 60 61 int main() { 62 63 // 初始化互斥量 64 pthread_mutex_init(&mutex, NULL); 65 66 // 创建3个子线程 67 pthread_t tid1, tid2, tid3; 68 pthread_create(&tid1, NULL, sellticket, NULL); 69 pthread_create(&tid2, NULL, sellticket, NULL); 70 pthread_create(&tid3, NULL, sellticket, NULL); 71 72 // 回收子线程的资源,阻塞 73 pthread_join(tid1, NULL); 74 pthread_join(tid2, NULL); 75 pthread_join(tid3, NULL); 76 77 pthread_exit(NULL); // 退出主线程 78 79 // 释放互斥量资源 80 pthread_mutex_destroy(&mutex); 81 82 return 0; 83 }
死锁
死锁
代码
deadlock.c
1 #include <stdio.h> 2 #include <pthread.h> 3 #include <unistd.h> 4 5 // 全局变量,所有的线程都共享这一份资源。 6 int tickets = 1000; 7 8 // 创建一个互斥量 9 pthread_mutex_t mutex; 10 11 void * sellticket(void * arg) { 12 13 // 卖票 14 while(1) { 15 16 // 加锁 17 pthread_mutex_lock(&mutex); 18 pthread_mutex_lock(&mutex); 19 20 if(tickets > 0) { 21 usleep(6000); 22 printf("%ld 正在卖第 %d 张门票\n", pthread_self(), tickets); 23 tickets--; 24 }else { 25 // 解锁 26 pthread_mutex_unlock(&mutex); 27 break; 28 } 29 30 // 解锁 31 pthread_mutex_unlock(&mutex); 32 pthread_mutex_unlock(&mutex); 33 } 34 35 return NULL; 36 } 37 38 int main() { 39 40 // 初始化互斥量 41 pthread_mutex_init(&mutex, NULL); 42 43 // 创建3个子线程 44 pthread_t tid1, tid2, tid3; 45 pthread_create(&tid1, NULL, sellticket, NULL); 46 pthread_create(&tid2, NULL, sellticket, NULL); 47 pthread_create(&tid3, NULL, sellticket, NULL); 48 49 // 回收子线程的资源,阻塞 50 pthread_join(tid1, NULL); 51 pthread_join(tid2, NULL); 52 pthread_join(tid3, NULL); 53 54 pthread_exit(NULL); // 退出主线程 55 56 // 释放互斥量资源 57 pthread_mutex_destroy(&mutex); 58 59 return 0; 60 }
deadlock1.c(多线程多锁造成的死锁场景)
1 #include <stdio.h> 2 #include <pthread.h> 3 #include <unistd.h> 4 5 // 创建2个互斥量 6 pthread_mutex_t mutex1, mutex2; 7 8 void * workA(void * arg) { 9 10 pthread_mutex_lock(&mutex1); 11 sleep(1); 12 pthread_mutex_lock(&mutex2); 13 14 printf("workA....\n"); 15 16 pthread_mutex_unlock(&mutex2); 17 pthread_mutex_unlock(&mutex1); 18 return NULL; 19 } 20 21 22 void * workB(void * arg) { 23 pthread_mutex_lock(&mutex2); 24 sleep(1); 25 pthread_mutex_lock(&mutex1); 26 27 printf("workB....\n"); 28 29 pthread_mutex_unlock(&mutex1); 30 pthread_mutex_unlock(&mutex2); 31 32 return NULL; 33 } 34 35 int main() { 36 37 // 初始化互斥量 38 pthread_mutex_init(&mutex1, NULL); 39 pthread_mutex_init(&mutex2, NULL); 40 41 // 创建2个子线程 42 pthread_t tid1, tid2; 43 pthread_create(&tid1, NULL, workA, NULL); 44 pthread_create(&tid2, NULL, workB, NULL); 45 46 // 回收子线程资源 47 pthread_join(tid1, NULL); 48 pthread_join(tid2, NULL); 49 50 // 释放互斥量资源 51 pthread_mutex_destroy(&mutex1); 52 pthread_mutex_destroy(&mutex2); 53 54 return 0; 55 }
读写锁
代码
1 /* 2 读写锁的类型 pthread_rwlock_t 3 int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr); 4 int pthread_rwlock_destroy(pthread_rwlock_t *rwlock); 5 int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock); 6 int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock); 7 int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock); 8 int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock); 9 int pthread_rwlock_unlock(pthread_rwlock_t *rwlock); 10 11 案例:8个线程操作同一个全局变量。 12 3个线程不定时写这个全局变量,5个线程不定时的读这个全局变量 13 */ 14 15 #include <stdio.h> 16 #include <pthread.h> 17 #include <unistd.h> 18 19 // 创建一个共享数据 20 int num = 1; 21 // pthread_mutex_t mutex; 22 pthread_rwlock_t rwlock; 23 24 void * writeNum(void * arg) { 25 26 while(1) { 27 pthread_rwlock_wrlock(&rwlock); 28 num++; 29 printf("++write, tid : %ld, num : %d\n", pthread_self(), num); 30 pthread_rwlock_unlock(&rwlock); 31 usleep(100); 32 } 33 34 return NULL; 35 } 36 37 void * readNum(void * arg) { 38 39 while(1) { 40 pthread_rwlock_rdlock(&rwlock); 41 printf("===read, tid : %ld, num : %d\n", pthread_self(), num); 42 pthread_rwlock_unlock(&rwlock); 43 usleep(100); 44 } 45 46 return NULL; 47 } 48 49 int main() { 50 51 pthread_rwlock_init(&rwlock, NULL); 52 53 // 创建3个写线程,5个读线程 54 pthread_t wtids[3], rtids[5]; 55 for(int i = 0; i < 3; i++) { 56 pthread_create(&wtids[i], NULL, writeNum, NULL); 57 } 58 59 for(int i = 0; i < 5; i++) { 60 pthread_create(&rtids[i], NULL, readNum, NULL); 61 } 62 63 // 设置线程分离 64 for(int i = 0; i < 3; i++) { 65 pthread_detach(wtids[i]); 66 } 67 68 for(int i = 0; i < 5; i++) { 69 pthread_detach(rtids[i]); 70 } 71 72 pthread_exit(NULL); 73 74 pthread_rwlock_destroy(&rwlock); 75 76 return 0; 77 }
生产者和消费者模型
该模型采用链表结构实现。
代码
1 /* 2 生产者消费者模型(粗略的版本) 3 */ 4 #include <stdio.h> 5 #include <pthread.h> 6 #include <stdlib.h> 7 #include <unistd.h> 8 9 // 创建一个互斥量 10 pthread_mutex_t mutex; 11 12 struct Node{ 13 int num; 14 struct Node *next; 15 }; 16 17 // 头结点 18 struct Node * head = NULL; 19 20 void * producer(void * arg) { 21 22 // 不断的创建新的节点,添加到链表中 23 while(1) { 24 pthread_mutex_lock(&mutex); 25 struct Node * newNode = (struct Node *)malloc(sizeof(struct Node)); 26 newNode->next = head; 27 head = newNode; 28 newNode->num = rand() % 1000; 29 printf("add node, num : %d, tid : %ld\n", newNode->num, pthread_self()); 30 pthread_mutex_unlock(&mutex); 31 usleep(100); 32 } 33 34 return NULL; 35 } 36 37 void * customer(void * arg) { 38 39 while(1) { 40 pthread_mutex_lock(&mutex); 41 // 保存头结点的指针 42 struct Node * tmp = head; 43 44 // 判断是否有数据 45 if(head != NULL) { 46 // 有数据 47 head = head->next; 48 printf("del node, num : %d, tid : %ld\n", tmp->num, pthread_self()); 49 free(tmp); 50 pthread_mutex_unlock(&mutex); 51 usleep(100); 52 } else { 53 // 没有数据 54 pthread_mutex_unlock(&mutex); 55 } 56 } 57 return NULL; 58 } 59 60 int main() { 61 62 pthread_mutex_init(&mutex, NULL); 63 64 // 创建5个生产者线程,和5个消费者线程 65 pthread_t ptids[5], ctids[5]; 66 67 for(int i = 0; i < 5; i++) { 68 pthread_create(&ptids[i], NULL, producer, NULL); 69 pthread_create(&ctids[i], NULL, customer, NULL); 70 } 71 72 for(int i = 0; i < 5; i++) { 73 pthread_detach(ptids[i]); 74 pthread_detach(ctids[i]); 75 } 76 77 while(1) { 78 sleep(10); 79 } 80 81 pthread_mutex_destroy(&mutex); 82 83 pthread_exit(NULL); 84 85 return 0; 86 }
条件变量
注意:解决线程同步的方法有互斥锁和读写锁,条件变量不是锁,条件变量不用来解决线程同步的问题。
代码
1 /* 2 条件变量的类型 pthread_cond_t 3 int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr); 4 int pthread_cond_destroy(pthread_cond_t *cond); 5 int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex); 6 - 等待,调用了该函数,线程会阻塞。 7 int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime); 8 - 等待多长时间,调用了这个函数,线程会阻塞,直到指定的时间结束。 9 int pthread_cond_signal(pthread_cond_t *cond); 10 - 唤醒一个或者多个等待的线程 11 int pthread_cond_broadcast(pthread_cond_t *cond); 12 - 唤醒所有的等待的线程 13 */ 14 #include <stdio.h> 15 #include <pthread.h> 16 #include <stdlib.h> 17 #include <unistd.h> 18 19 // 创建一个互斥量 20 pthread_mutex_t mutex; 21 // 创建条件变量 22 pthread_cond_t cond; 23 24 struct Node{ 25 int num; 26 struct Node *next; 27 }; 28 29 // 头结点 30 struct Node * head = NULL; 31 32 void * producer(void * arg) { 33 34 // 不断的创建新的节点,添加到链表中 35 while(1) { 36 pthread_mutex_lock(&mutex); 37 struct Node * newNode = (struct Node *)malloc(sizeof(struct Node)); 38 newNode->next = head; 39 head = newNode; 40 newNode->num = rand() % 1000; 41 printf("add node, num : %d, tid : %ld\n", newNode->num, pthread_self()); 42 43 // 只要生产了一个,就通知消费者消费 44 pthread_cond_signal(&cond); 45 46 pthread_mutex_unlock(&mutex); 47 usleep(100); 48 } 49 50 return NULL; 51 } 52 53 void * customer(void * arg) { 54 55 while(1) { 56 pthread_mutex_lock(&mutex); 57 // 保存头结点的指针 58 struct Node * tmp = head; 59 // 判断是否有数据 60 if(head != NULL) { 61 // 有数据 62 head = head->next; 63 printf("del node, num : %d, tid : %ld\n", tmp->num, pthread_self()); 64 free(tmp); 65 pthread_mutex_unlock(&mutex); 66 usleep(100); 67 } else { 68 // 没有数据,需要等待 69 // 当这个函数调用阻塞的时候,会对互斥锁进行解锁,当不阻塞的,继续向下执行,会重新加锁。 70 pthread_cond_wait(&cond, &mutex); 71 pthread_mutex_unlock(&mutex); 72 } 73 } 74 return NULL; 75 } 76 77 int main() { 78 79 pthread_mutex_init(&mutex, NULL); 80 pthread_cond_init(&cond, NULL); 81 82 // 创建5个生产者线程,和5个消费者线程 83 pthread_t ptids[5], ctids[5]; 84 85 for(int i = 0; i < 5; i++) { 86 pthread_create(&ptids[i], NULL, producer, NULL); 87 pthread_create(&ctids[i], NULL, customer, NULL); 88 } 89 90 for(int i = 0; i < 5; i++) { 91 pthread_detach(ptids[i]); 92 pthread_detach(ctids[i]); 93 } 94 95 while(1) { 96 sleep(10); 97 } 98 99 pthread_mutex_destroy(&mutex); 100 pthread_cond_destroy(&cond); 101 102 pthread_exit(NULL); 103 104 return 0; 105 }
信号量(信号灯)
注意:单独使用信号量不能让线程安全,需要配合使用互斥锁才能保证线程的安全。
信号量的类型
代码
1 /* 2 信号量的类型 sem_t 3 int sem_init(sem_t *sem, int pshared, unsigned int value); 4 - 初始化信号量 5 - 参数: 6 - sem : 信号量变量的地址 7 - pshared : 0 用在线程间 ,非0 用在进程间 8 - value : 信号量中的值 9 10 int sem_destroy(sem_t *sem); 11 - 释放资源 12 13 int sem_wait(sem_t *sem); 14 - 对信号量加锁,调用一次对信号量的值-1,如果值为0,就阻塞 15 16 int sem_trywait(sem_t *sem); 17 18 int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout); 19 int sem_post(sem_t *sem); 20 - 对信号量解锁,调用一次对信号量的值+1 21 22 int sem_getvalue(sem_t *sem, int *sval); 23 24 sem_t psem; 25 sem_t csem; 26 init(psem, 0, 8); 27 init(csem, 0, 0); 28 29 producer() { 30 sem_wait(&psem); 31 sem_post(&csem) 32 } 33 34 customer() { 35 sem_wait(&csem); 36 sem_post(&psem) 37 } 38 39 */ 40 41 #include <stdio.h> 42 #include <pthread.h> 43 #include <stdlib.h> 44 #include <unistd.h> 45 #include <semaphore.h> 46 47 // 创建一个互斥量 48 pthread_mutex_t mutex; 49 // 创建两个信号量 50 sem_t psem; 51 sem_t csem; 52 53 struct Node{ 54 int num; 55 struct Node *next; 56 }; 57 58 // 头结点 59 struct Node * head = NULL; 60 61 void * producer(void * arg) { 62 63 // 不断的创建新的节点,添加到链表中 64 while(1) { 65 sem_wait(&psem); 66 pthread_mutex_lock(&mutex); 67 struct Node * newNode = (struct Node *)malloc(sizeof(struct Node)); 68 newNode->next = head; 69 head = newNode; 70 newNode->num = rand() % 1000; 71 printf("add node, num : %d, tid : %ld\n", newNode->num, pthread_self()); 72 pthread_mutex_unlock(&mutex); 73 sem_post(&csem); 74 } 75 76 return NULL; 77 } 78 79 void * customer(void * arg) { 80 81 while(1) { 82 sem_wait(&csem); 83 pthread_mutex_lock(&mutex); 84 // 保存头结点的指针 85 struct Node * tmp = head; 86 head = head->next; 87 printf("del node, num : %d, tid : %ld\n", tmp->num, pthread_self()); 88 free(tmp); 89 pthread_mutex_unlock(&mutex); 90 sem_post(&psem); 91 92 } 93 return NULL; 94 } 95 96 int main() { 97 98 pthread_mutex_init(&mutex, NULL); 99 sem_init(&psem, 0, 8); 100 sem_init(&csem, 0, 0); 101 102 // 创建5个生产者线程,和5个消费者线程 103 pthread_t ptids[5], ctids[5]; 104 105 for(int i = 0; i < 5; i++) { 106 pthread_create(&ptids[i], NULL, producer, NULL); 107 pthread_create(&ctids[i], NULL, customer, NULL); 108 } 109 110 for(int i = 0; i < 5; i++) { 111 pthread_detach(ptids[i]); 112 pthread_detach(ctids[i]); 113 } 114 115 while(1) { 116 sleep(10); 117 } 118 119 pthread_mutex_destroy(&mutex); 120 121 pthread_exit(NULL); 122 123 return 0; 124 }
雪儿言