lee-romantic 's Blog
Everything is OK!
Toggle navigation
lee-romantic 's Blog
主页
About Me
归档
标签
Linux多线程相关知识点
2020-06-14 23:00:15
503
0
0
lee-romantic
# 1、线程的创建 - 头文件`#include <pthread.h>` - 在Linux中,通过函数pthread_create()函数实现线程的创建: ``` int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg); ``` - `pthread_create`参数 - thread表示的是一个pthread_t类型的指针; - attr用于指定线程的一些属性; - start_routine表示的是一个函数指针,该函数是线程调用函数;`传参时,可以用取地址符&函数名,比如&myFunc,当然也可以直接传函数名。` - arg表示的是传递给线程调用函数的参数。 - 当线程创建成功时,函数`pthread_create()`返回0,若返回值不为0则表示创建线程失败。对于线程的属性,则在结构体`pthread_attr_t`中定义。 - g++编译运行需要使用`-lpthread`参数: ``` g++ test.cpp -lpthread -o test.o ``` # 2、线程终止 - 终止某个线程而不终止整个进程有下面三种方法: - 在线程函数中`return`。这种方法对主线程不适用,从main函数return相当于调用exit。//线程return - 线程自己调用`pthread_exit`来终止自己,主动终止: `int pthread_exit(void* retval); `,注意:retval不能指向该线程的栈空间,否则可能成为野指针 - 一个线程调用`pthread_cancel`来终止另外一个线程,被动终止 - 终止某个线程并终止所有的线程: - 任意线程调用了`exit()`,或者主线程执行了`return`语句(即在`main()`函数中),都会导致进程中的所有线程立即终止。 # 3、线程等待 - 当一个线程退出时,如果空间没有被释放,新创建的线程也不会利用退出线程的资源,所以会发生内存泄漏。因此新线程在退出时主线程要通过`等待的方式回收退出现成的资源并获取新线程退出时的状态`,或者分离也可以安全地释放资源。 - 一个线程仅允许一个线程使用pthread_join()等待它的终止。 ``` int pthread_join(pthread_t pthread,void** retval); ``` - 参数: - `pthread`为主线程要等待的进程 - `retval`为一指针,指向新线程退出码所在的空间,即为新线程退出码的地址 - 返回值:成功返回0,失败返回错误码 - 注意:该函数是以阻塞的方式进行等待的, thread线程以不同的方式终止,得到的线程终止状态也是不同的: - (1)线程以`return`的方式终止,`retval`指向的空间中保存`return`返回的值 - (2)线程以`pthread_exit`的方式终止,`retval`指向的空间中保存该函数的参数 - (3)线程以`pthread_cancel`的方式终止,`retval`指向的空间中保存常数`PTHREAD_CANCELED`其实为-1.即`#define PTHREAD_CANCELED (void*)-1;` - (4)如果不关心新线程的退出状态,直接将`retval`设置为`NULL`即可。例子: ``` #include<stdio.h> #include<pthread.h> #include<unistd.h> void* pthread1(void* arg) { printf("i am phread1\n"); return (void*)1; } void* pthread2(void* arg) { printf("i am phread2\n"); pthread_exit((void*)2); } void* pthread3(void* arg) { printf("i am phread3\n"); while(1) { sleep(1); } return (void*)5; } int main() { pthread_t tid; void* ret; printf("main tid : %x\n",pthread_self()); //以return方式退出 pthread_create(&tid,NULL,pthread1,NULL); pthread_join(tid,&ret); printf("pthread1 tid:%x return code: %d\n",tid,(int)ret); //以pthread_exit方式退出 pthread_create(&tid,NULL,pthread2,NULL); pthread_join(tid,&ret); printf("pthread1 tid:%x return code: %d\n",tid,(int)ret); //以pthread_cancel方式退出 pthread_create(&tid,NULL,pthread3,NULL); pthread_cancel(tid); pthread_join(tid,&ret); printf("pthread3 id:%x return code: %d\n",tid,(int)ret); return 0; } ``` 对于线程进行`join`之后线程的状态将是`detach`状态(分离),同样的`pthread_cancel`函数也可以对线程进行分离处理。不能同时对一个线程进行`join`和`detach`操作。 当线程X连接线程Y时,如果线程Y仍在运行,则线程X会阻塞直到线程Y终止;如果线程Y在被连接之前已经终止了,那么线程X的连接调用会立即返回。 连接线程其实还有另外一层意义,一个线程终止后,如果没有人对它进行连接,那么该终止线程占用的资源,系统将无法回收,而该终止线程也会成为僵尸线程。因此,当我们去连接某个线程时,其实也是在告诉系统该终止线程的资源可以回收了。 注意: **对于一个已经被连接过的线程再次执行连接操作, 将会导致无法预知的行为!** # 4、线程分离 - 如果主线程不关心新线程的退出状态。这时可以对线程进行分离,使新线程退出时,自动释放资源。 ``` int pthread_detach(pthread_t thread); ``` - 参数 - 要分离的线程id。成功返回0,失败返回错误码。 - 可以是线程组内的其他线程对目标线程进行分离,也可以自己分离。 - 注意: - 新创建的线程默认是不可分离的,即可结合的。 在任意一个时间点上,线程要么是可结合`(joinable)`,要么是可分离的`(detached)`。一个可结合线程是可以被其他线程收回资源和杀死的。在被回收之前,他的存储器资源(栈等)是不释放的。而对于`detached`状态的线程,其资源不能被别的线程收回和杀死,只有等到线程结束才能由系统自动释放。 - 默认情况,线程状态被设置为结合的。所以为了避免资源泄漏等问题,一个线程应当是被显示的join或者detach的,否则线程的状态类似于进程中的Zombie Process,会有部分资源没有被回收。 - 一般情况下,线程终止后,其终止状态一直保留到其它线程调用`pthread_join`获取它的状态为止。但是线程也可以被置为`detach`状态,这样的线程一旦终止就立刻回收它占用的所有资源,而不保留终止状态。不能对一个已经处于`detach`状态的线程调用`pthread_join`,这样的调用将返回EINVAL错误。也就是说,如果已经对一个线程调用了`pthread_detach`就不能再调用`pthread_join`了。 - 一个分离的线程异常退出,整个进程也会异常退出。 - pthread_join的阻塞问题: - 调用函数pthread_join,当等待线程没有终止时,主线程将处于阻塞状态,直到线程以某种方式终止。 - 如果让线程处于detached状态,则不能进行join连接,并且此时主线程因为不再调用join也可以使得线程安全释放资源,此时主线程变为非阻塞的: - 在主线程中加入代码: `pthread_detach(thread_id)` - 或者在被等待线程中加入: `pthread_detach(thread_self())` - 总结: - 1.linux线程执行和windows不同,pthread有两种状态`joinable`状态和`unjoinable`状态,如果线程是joinable状态,当线程函数自己返回退出时或pthread_exit时都不会释放线程所占用堆栈和线程描述符(总计8K多)。只有当你调用了pthread_join之后这些资源才会被释放。若是unjoinable状态的线程,这些资源在线程函数退出时或pthread_exit时自动会被释放。 - 2.unjoinable属性可以在`pthread_create`时指定,或在线程创建后在线程中`pthread_detach`自己, 如:`pthread_detach(pthread_self())`,将状态改为unjoinable状态,确保资源的释放。或者将线程置为 joinable,然后适时调用`pthread_join`. - 3.其实简单的说就是在线程函数头加上 `pthread_detach(pthread_self())`的话,线程状态改变,在函数尾部直接` pthread_exit`线程就会自动退出, 省去了给线程擦屁股的麻烦。 - 进程线程控制原语对比: | 进程 | 线程 | |:---:|:----:| |fork |pthread_create| |exit |pthread_exit | |wait | pthread_join| | kill | pthread_cancel| | getpid | pthread_self命名空间| 参考代码: ``` /* * 文件名: thread_sample1.c * 描述:演示线程基本操作 */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <pthread.h> /*子线程1入口函数*/ void* thread_routine1(void *arg) { fprintf(stdout, "thread1: hello world!\n"); sleep(1); /*子线程1在此退出*/ return NULL; } /*子线程2入口函数*/ void* thread_routine2(void *arg) { fprintf(stdout, "thread2: I'm running...\n"); pthread_t main_thread = (pthread_t)arg; /*分离自我,不能再被连接*/ pthread_detach(pthread_self()); /*判断主线程ID与子线程2ID是否相等*/ if (!pthread_equal(main_thread, pthread_self())) { fprintf(stdout, "thread2: main thread id is not equal thread2\n"); } /*等待主线程终止*/ pthread_join(main_thread, NULL); fprintf(stdout, "thread2: main thread exit!\n"); fprintf(stdout, "thread2: exit!\n"); fprintf(stdout, "thread2: process exit!\n"); /*子线程2在此终止,进程退出*/ pthread_exit(NULL); } int main(int argc, char *argv[]) { /*创建子线程1*/ pthread_t t1; if (pthread_create(&t1, NULL, thread_routine1, NULL)!=0) { fprintf(stderr, "create thread fail.\n"); exit(-1); } /*等待子线程1终止*/ pthread_join(t1, NULL); fprintf(stdout, "main thread: thread1 terminated!\n\n"); /*创建子线程2,并将主线程ID传递给子线程2*/ pthread_t t2; if (pthread_create(&t2, NULL, thread_routine2, (void *)pthread_self())!=0) { fprintf(stderr, "create thread fail.\n"); exit(-1); } fprintf(stdout, "main thread: sleeping...\n"); sleep(3); /*主线程使用pthread_exit函数终止,进程继续存在*/ fprintf(stdout, "main thread: exit!\n"); pthread_exit(NULL); fprintf(stdout, "main thread: never reach here!\n"); return 0; } ``` 可参考:https://blog.csdn.net/taoyanqi8932/article/details/56288950 # 5、同步与互斥 - 同步 是指散布在不同任务之间的若干程序片断,它们的运行必须严格按照规定的某种先后次序来运行,这种先后次序依赖于要完成的特定的任务。最基本的场景就是:两个或两个以上的进程或线程在运行过程中协同步调,按预定的先后次序运行。比如 A 任务的运行依赖于 B 任务产生的数据。 - 互斥 是指散布在不同任务之间的若干程序片断,当某个任务运行其中一个程序片段时,其它任务就不能运行它们之中的任一程序片段,只能等到该任务运行完这个程序片段后才可以运行。最基本的场景就是:一个公共资源同一时刻只能被一个进程或线程使用,多个进程或线程不能同时使用公共资源。 ## 5.1、互斥锁 - 在访问共享资源后临界区域前,对互斥锁进行加锁; - 在访问完成后释放互斥锁导上的锁。在访问完成后释放互斥锁导上的锁; - 对互斥锁进行加锁后,任何其他试图再次对互斥锁加锁的线程将会被阻塞,直到锁被释放。对互斥锁进行加锁后,任何其他试图再次对互斥锁加锁的线程将会被阻塞,直到锁被释放。 ``` #include <pthread.h> #include <time.h> //使用PTHREAD_MUTEX_INITIALIZER静态初始化,且携带默认属性 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; // 初始化一个互斥锁。 int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr); // 对互斥锁上锁,若互斥锁已经上锁,则调用者一直阻塞,直到互斥锁解锁后再上锁。 int pthread_mutex_lock(pthread_mutex_t *mutex); // 调用该函数时,若互斥锁未加锁,则上锁,返回 0;若互斥锁已加锁,则函数直接返回失败,即EBUSY。 int pthread_mutex_trylock(pthread_mutex_t *mutex); // 当线程试图获取一个已加锁的互斥量时,pthread_mutex_timedlock 互斥量 // 原语允许绑定线程阻塞时间。即非阻塞加锁互斥量。 int pthread_mutex_timedlock(pthread_mutex_t *restrict mutex, const struct timespec *restrict abs_timeout); // 对指定的互斥锁解锁。 int pthread_mutex_unlock(pthread_mutex_t *mutex); // 销毁指定的一个互斥锁。互斥锁在使用完毕后,必须要对互斥锁进行销毁,以释放资源。 int pthread_mutex_destroy(pthread_mutex_t *mutex); 原文链接:https://blog.csdn.net/daaikuaichuan/article/details/82950711 ``` 代码示例: ``` //原链接地址 //http://zhangxiaoya.github.io/2015/05/15/multi-thread-of-c-program-language-on-linux/ #include <stdio.h> #include <stdlib.h> #include <pthread.h> int sharei = 0; void increase_num(void); // add mutex pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; int main() { int ret; pthread_t thread1,thread2,thread3; ret = pthread_create(&thread1,NULL,(void *)&increase_num,NULL); ret = pthread_create(&thread2,NULL,(void *)&increase_num,NULL); ret = pthread_create(&thread3,NULL,(void *)&increase_num,NULL); pthread_join(thread1,NULL); pthread_join(thread2,NULL); pthread_join(thread3,NULL); printf("sharei = %d\n",sharei); return 0; } void increase_num(void) { long i,tmp; for(i =0;i<=10000;++i) { // lock if(pthread_mutex_lock(&mutex) != 0) { perror("pthread_mutex_lock"); exit(EXIT_FAILURE); } tmp = sharei; tmp = tmp + 1; sharei = tmp; // unlock if(pthread_mutex_unlock(&mutex) != 0) { perror("pthread_mutex_unlock"); exit(EXIT_FAILURE); } } } ``` ## 5.2、条件变量(生产者消费者模型) - 与互斥锁不同,条件变量是用来等待而不是用来上锁的。条件变量用来自动阻塞一个线程,直 到某特殊情况发生为止。通常条件变量和互斥锁同时使用。 - 条件变量使我们可以睡眠等待某种条件出现。条件变量是利用线程间共享的全局变量进行同步 的一种机制,主要包括两个动作: - 一个线程等待"条件变量的条件成立"而挂起; - 另一个线程使 “条件成立”(给出条件成立信号)。 ``` #include <pthread.h> // 初始化条件变量 int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr); 参数: - cond: 条件变量的地址 - attr: 使用默认属性, 这个值设置为NULL // 释放资源 int pthread_cond_destroy(pthread_cond_t *cond); // 线程调用该函数之后, 先解锁互斥量mutex,然后阻塞等待,唤醒后重新锁定mutex int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex); 参数: - cond: 条件变量 - mutex: 互斥锁 struct timespec { time_t tv_sec; /* Seconds */ long tv_nsec; /* Nanoseconds [0 .. 999999999] */ }; // 在指定的时间之后解除阻塞 int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime); 参数: - cond: 条件变量 - mutex: 互斥锁 - abstime: 阻塞的时间 - 当前时间 + 要阻塞的时长 struct timeval val; 可以使用函数:gettimeofday(&val, NULL); // 唤醒一个或多个阻塞在 pthread_cond_wait / pthread_cond_timedwait 函数上的线程 int pthread_cond_signal(pthread_cond_t *cond); // 唤醒所有的阻塞在 pthread_cond_wait / pthread_cond_timedwait 函数上的线程 int pthread_cond_broadcast(pthread_cond_t *cond); ``` 使用互斥量+条件变量实现生产者消费者模型: ``` //https://blog.csdn.net/qq_35883464/article/details/103547949#4.%20%E8%AF%BB%E5%86%99%E9%94%81 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/stat.h> #include <sys/types.h> #include <string.h> #include <pthread.h> //节点结构 typedef struct node { int data; struct node *next; }Node; //全局资源 Node* head = NULL; //线程同步-互斥锁 pthread_mutex_t mutex; //阻塞线程-条件变量类型的变量 pthread_cond_t cond; void *producer(void *arg) { while(1) { //创建一个链表节点 Node* pnew = (Node*)malloc(sizeof(Node)); //节点初始化 pnew->data = rand() % 1000; //使用互斥锁保护共享数据 pthread_mutex_lock(&mutex); //指针域 pnew->next = head; head = pnew; printf("---producer = %lu, %d\n",pthread_self(),pnew->data);//%lu表示输出无符号长整型整数 (long unsigned) pthread_mutex_unlock(&mutex); //通知阻塞的消费者解除阻塞 pthread_cond_signal(&cond); sleep(rand() % 3); } return NULL; } void *customer(void *arg) { while(1) { pthread_mutex_lock(&mutex); //判断链表是否为空 if(head == NULL) { //线程阻塞,并且该wait函数将会对互斥锁解锁,这里就避免了再去做无谓的mutex锁的竞争 pthread_cond_wait(&cond,&mutex); //条件满足,解除阻塞后,对互斥锁做加锁操作 } //链表不为空,删掉一个节点,删掉头结点 Node* pdel = head; head = head->next; printf("---customer = %lu, %d\n",pthread_self(),pdel->data); free(pdel); sleep(rand() % 3); pthread_mutex_unlock(&mutex); } return NULL; } int main(int argc,const char *argv[]) { pthread_t p1,p2; //初始化 pthread_mutex_init(&mutex,NULL); pthread_cond_init(&cond,NULL); //创建生产者线程 pthread_create(&p1,NULL,producer,NULL); //创建消费者线程 pthread_create(&p2,NULL,customer,NULL); //阻塞回收子线程 pthread_join(p1,NULL); pthread_join(p2,NULL); return 0; } ``` ## 5.3、读写锁 一次只有一个线程可以占有写模式的读写锁,但是多个线程可以同时占有读模式的读写锁(允许多个线程读但只允许一个线程写) ``` //https://blog.csdn.net/daaikuaichuan/article/details/82950711 #include <pthread.h> // 初始化读写锁 int pthread_rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr); // 申请读锁 int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock ); // 申请写锁 int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock ); // 尝试以非阻塞的方式来在读写锁上获取写锁, // 如果有任何的读者或写者持有该锁,则立即失败返回。 int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock); // 解锁 int pthread_rwlock_unlock (pthread_rwlock_t *rwlock); // 销毁读写锁 int pthread_rwlock_destroy(pthread_rwlock_t *rwlock); ``` ``` // 一个使用读写锁来实现 4 个线程读写一段数据是实例。 // 在此示例程序中,共创建了 4 个线程, // 其中两个线程用来写入数据,两个线程用来读取数据 #include <stdio.h> #include <unistd.h> #include <pthread.h> pthread_rwlock_t rwlock; //读写锁 int num = 1; //读操作,其他线程允许读操作,却不允许写操作 void *fun1(void *arg) { while(1) { pthread_rwlock_rdlock(&rwlock); printf("read num first == %d\n", num); pthread_rwlock_unlock(&rwlock); sleep(1); } } //读操作,其他线程允许读操作,却不允许写操作 void *fun2(void *arg) { while(1) { pthread_rwlock_rdlock(&rwlock); printf("read num second == %d\n", num); pthread_rwlock_unlock(&rwlock); sleep(2); } } //写操作,其它线程都不允许读或写操作 void *fun3(void *arg) { while(1) { pthread_rwlock_wrlock(&rwlock); num++; printf("write thread first\n"); pthread_rwlock_unlock(&rwlock); sleep(2); } } //写操作,其它线程都不允许读或写操作 void *fun4(void *arg) { while(1) { pthread_rwlock_wrlock(&rwlock); num++; printf("write thread second\n"); pthread_rwlock_unlock(&rwlock); sleep(1); } } int main() { pthread_t ptd1, ptd2, ptd3, ptd4; pthread_rwlock_init(&rwlock, NULL);//初始化一个读写锁 //创建线程 pthread_create(&ptd1, NULL, fun1, NULL); pthread_create(&ptd2, NULL, fun2, NULL); pthread_create(&ptd3, NULL, fun3, NULL); pthread_create(&ptd4, NULL, fun4, NULL); //等待线程结束,回收其资源 pthread_join(ptd1, NULL); pthread_join(ptd2, NULL); pthread_join(ptd3, NULL); pthread_join(ptd4, NULL); pthread_rwlock_destroy(&rwlock);//销毁读写锁 return 0; } ``` ## 5.4、自旋锁 自旋锁与互斥量功能一样,唯一一点不同的就是互斥量阻塞后休眠让出cpu,而自旋锁阻塞后不会让出cpu,会一直忙等待,直到得到锁。 自旋锁在用户态使用的比较少,在内核使用的比较多!自旋锁的使用场景:锁的持有时间比较短,或者说小于2次上下文切换的时间。 自旋锁在用户态的函数接口和互斥量一样,把pthread_mutex_xxx()中mutex换成spin,如:pthread_spin_init()。 ## 5.5、信号量 锁机制使用是有限制的,锁只有两种状态,即加锁和解锁,对于互斥的访问一个全局变量,这样的方式还可以对付,但是要是对于其他的临界资源,比如说多台打印机等,这种方式显然不行了。 信号量机制在操作系统里面学习的比较熟悉了,信号量是一个整数计数器,其数值表示空闲临界资源的数量。 当有进程释放资源时,信号量增加,表示可用资源数增加;当有进程申请到资源时,信号量减少,表示可用资源数减少。这个时候可以把锁机制认为是0-1信号量。 关于信号量机制的函数。 http://zhangxiaoya.github.io/2015/05/15/multi-thread-of-c-program-language-on-linux/ ``` //https://blog.csdn.net/daaikuaichuan/article/details/82950711 #include <semaphore.h> // 初始化信号量 int sem_init(sem_t *sem, int pshared, unsigned int value); // 信号量 P 操作(减 1) int sem_wait(sem_t *sem); // 以非阻塞的方式来对信号量进行减 1 操作 int sem_trywait(sem_t *sem); // 信号量 V 操作(加 1) int sem_post(sem_t *sem); // 获取信号量的值 int sem_getvalue(sem_t *sem, int *sval); // 销毁信号量 int sem_destroy(sem_t *sem); ``` 使用信号量实现消费者模型: ``` #include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <semaphore.h> #define MAXSIZE 10 int stack[MAXSIZE]; int size =0; sem_t sem; void privide_data(void) { int i; for(i =0;i<MAXSIZE;++i) { stack[i] = i; sem_post(&sem); } } void handle_data(void) { int i; while((i = size ++) <MAXSIZE) { sem_wait(&sem); printf("cross : %d X %d = %d \n",stack[i],stack[i],stack[i] * stack[i]); sleep(1); } } int main() { pthread_t privider,handler; sem_init(&sem,0,0); pthread_create(&privider,NULL,(void *)&privide_data,NULL); pthread_create(&handler,NULL,(void *)&handle_data,NULL); pthread_join(privider,NULL); pthread_join(handler,NULL); sem_destroy(&sem); return 0; } ``` 信号量用于同步实例: ``` // 信号量用于同步实例 #include <stdio.h> #include <unistd.h> #include <pthread.h> #include <semaphore.h> sem_t sem_g,sem_p; //定义两个信号量 char ch = 'a'; void *pthread_g(void *arg) //此线程改变字符ch的值 { while(1) { sem_wait(&sem_g); ch++; sleep(1); sem_post(&sem_p); } } void *pthread_p(void *arg) //此线程打印ch的值 { while(1) { sem_wait(&sem_p); printf("%c",ch); fflush(stdout); sem_post(&sem_g); } } int main(int argc, char *argv[]) { pthread_t tid1,tid2; sem_init(&sem_g, 0, 0); // 初始化信号量为0 sem_init(&sem_p, 0, 1); // 初始化信号量为1 // 创建两个线程 pthread_create(&tid1, NULL, pthread_g, NULL); pthread_create(&tid2, NULL, pthread_p, NULL); // 回收线程 pthread_join(tid1, NULL); pthread_join(tid2, NULL); return 0; } 原文链接:https://blog.csdn.net/daaikuaichuan/article/details/82950711 ``` 信号量用于互斥实例: ``` // 信号量用于互斥实例 #include <stdio.h> #include <pthread.h> #include <unistd.h> #include <semaphore.h> sem_t sem; //信号量 void printer(char *str) { sem_wait(&sem);//减一,p操作 while(*str) // 输出字符串(如果不用互斥,此处可能会被其他线程入侵) { putchar(*str); fflush(stdout); str++; sleep(1); } printf("\n"); sem_post(&sem);//加一,v操作 } void *thread_fun1(void *arg) { char *str1 = "hello"; printer(str1); } void *thread_fun2(void *arg) { char *str2 = "world"; printer(str2); } int main(void) { pthread_t tid1, tid2; sem_init(&sem, 0, 1); //初始化信号量,初始值为 1 //创建 2 个线程 pthread_create(&tid1, NULL, thread_fun1, NULL); pthread_create(&tid2, NULL, thread_fun2, NULL); //等待线程结束,回收其资源 pthread_join(tid1, NULL); pthread_join(tid2, NULL); sem_destroy(&sem); //销毁信号量 return 0; } 原文链接:https://blog.csdn.net/daaikuaichuan/article/details/82950711 ``` # 6、总结(线程同步的方式) - 线程同步方式: - 互斥量: 采用互斥对象机制,比如`pthread.h`中的`pthread_mutex_t`,`pthread_rwlock_t`等,只有拥有互斥对象的线程才有访问公共资源的权限。因为公共资源只有一个,所以可以保证公共资源不会被多个线程同时访问。 - 条件变量 与互斥锁不同,条件变量是用来等待而不是用来上锁的。条件变量用来自动阻塞一个线程,直到某特殊情况发生为止。通常条件变量和互斥锁同时使用。条件变量使我们可以睡眠等待某种条件出现。条件变量是利用线程间共享的全局变量进行同步 的一种机制,主要包括两个动作: - 一个线程等待"条件变量的条件成立"而挂起; - 另一个线程使 “条件成立”(给出条件成立信号)。 - 信号量 - 锁机制使用是有限制的,锁只有两种状态,即加锁和解锁,对于互斥的访问一个全局变量,这样的方式还可以对付,但是要是对于其他的临界资源,比如说多台打印机等,这种方式显然不行了。 - 信号量是一个整数计数器,其数值表示空闲临界资源的数量。 当有进程释放资源时,信号量增加,表示可用资源数增加;当有进程申请到资源时,信号量减少,表示可用资源数减少。这个时候可以把锁机制认为是0-1信号量。 - 信号(事件,通知) 通过通知操作的方式来保持多线程同步,还可以实现多线程优先级的比较操作。 # 7. C++11的多线程写法 参考: https://blog.csdn.net/weixin_36049506/article/details/93333549 两个比较常见的C++多线程面试代码题: https://www.cnblogs.com/z1014601153/p/11350631.html
上一篇:
二分查找的模板写法
下一篇:
排序算法总结
0
赞
503 人读过
新浪微博
微信
腾讯微博
QQ空间
人人网
提交评论
立即登录
, 发表评论.
没有帐号?
立即注册
0
条评论
More...
文档导航
没有帐号? 立即注册