-
线程的销毁
-
自然死亡
从线程函数返回,线程正常退出,被主线程调用pthread_join回收。
-
自杀
调用 pthread_exit 自杀。
-
他杀
调用 pthread_cancel 来终止某个线程。
-
非正常死亡
在线程的函数里面抛出异常,或者segfault的信号的时候。
-
-
互斥锁
下面是一个例子,3个线程如果不加锁,就会出现race condition。
#include <iostream> #include <pthread.h> using namespace std; const int thread_num = 3; pthread_mutex_t mutex; int sum = 0; //注意,线程调用的函数的参数和返回值,必须是 void * arg 和 void * void* thread_fun_with_lock(void * args) { // int ret = pthread_mutex_lock(&mutex); //返回0为成功 // if(ret) // { // printf("Thread %d lock failed\n",no); // pthread_exit(NULL); // } for(int i=0;i<1000000;i++) { pthread_mutex_lock(&mutex); sum=sum+1; pthread_mutex_unlock(&mutex); } } void* thread_fun(void * args) { for(int i=0;i<1000000;i++) //100w { sum=sum+1; } } int main() { pthread_t thread[thread_num]; pthread_mutex_init(&mutex,NULL); for(int i=0;i<thread_num;i++) { pthread_create(&thread[i],NULL,thread_fun_with_lock,NULL); } for(int i=0;i<thread_num;i++) { pthread_join(thread[i],NULL); } pthread_mutex_destroy(&mutex); cout<<"sum : "<<sum<<endl; return 0; }
-
读写锁
-
条件变量
条件变量适合的场景是wait和notify的场景。
给一个条件变量的例子。
#include <iostream> #include <queue> #include <cstdlib> #include <unistd.h> #include <pthread.h> using namespace std; //把共享数据和它们的同步变量集合到一个结构中,这往往是一个较好的编程技巧。 struct{ pthread_mutex_t mutex; pthread_cond_t cond; queue<int> product; }sharedData = {PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER}; void * produce(void *ptr) { for (int i = 0; i < 10; ++i) { pthread_mutex_lock(&sharedData.mutex); sharedData.product.push(i); pthread_mutex_unlock(&sharedData.mutex); //如果队列里面有一个元素,就通知消费者线程去消耗 if (sharedData.product.size() == 1) pthread_cond_signal(&sharedData.cond); } } void * consume(void *ptr) { for (int i = 0; i < 10;) { pthread_mutex_lock(&sharedData.mutex); //如果队列是空的,那么就等待 while(sharedData.product.empty()) pthread_cond_wait(&sharedData.cond, &sharedData.mutex); ++i; cout<<"consume:"<<sharedData.product.front()<<endl; sharedData.product.pop(); pthread_mutex_unlock(&sharedData.mutex); } } int main() { pthread_t tid1, tid2; pthread_create(&tid1, NULL, consume, NULL); pthread_create(&tid2, NULL, produce, NULL); pthread_join(tid1, NULL); pthread_join(tid2, NULL); return 0; }
-
信号量
多线程同步的信号量和ipc用于进程同步的信号量的语义是不一样的。
在陈硕的《linux多线程服务器编程》第二章里面,并不提倡使用这样方式进行线程同步。同时,也不提倡使用读写锁在同步。
-
多线程环境
-
可重入函数
如果一个函数可以被多个线程调用,但是不发生race_condititon ,那么这个函数就是可重入的函数。
-
线程和进程
在一个线程的函数里面执行fork的操作(在linux的环境下),它只会复制当前的thread,而不会克隆其他的线程。
这个时候,有一个很危险的场景。一个线程之后了fork之后,fork产生的子进程只有一个线程。如果其他线程正好拿到一个锁,而他突然消失,就再也没有机会解锁。就会造成死锁。
-
线程和信号
每个线程可以独立的设置信号的掩码。
int pthread_sigmask( int how, const sigset_t * newmask, sigset_t * oldmask );
个人的一个理解,在多线程的环境下 使用fork和信号,都是很危险的动作。
-
-
线程池
-
互斥锁的属性
-
PTHREAD_PROCESS_SHARED
互斥锁可以被跨进程共享
-
PTHREAD_PROCESS_PRIVATE
互斥锁只能在同一个进程下面的线程之间共享
-
-
互斥锁的type类型
看这个之前,思考一个问题,如果在一个函数里面lock之后,再lock一次,会出现什么情况?这个好像叫做锁的可重入性
比如,如下的代码:
void* test_re_lock(void * args) { //获得锁 pthread_mutex_lock(&mutex); //再一次获得锁 pthread_mutex_lock(&mutex); cout<<"test"<<endl; }
因为linux下面默认的锁的类型就是普通锁,如果执行上面的代码,将会出现死锁的情况。
Linux下面有4中类型的锁。
-
PTHREAD_PROCESS_NORMAL 普通锁,这个是默认的锁的类型。
-
如果一个线程获得锁了之后,再一次执行获得锁的操作,那么就会导致死锁。
-
如果一个线程获得了锁,其他线程请求锁,那么会形成一个阻塞队列。
-
如果一个线程获得了锁,其他线程再次加锁/或是其他线程解锁,将导致不可预期的结果。
-
如果对一个已经解锁的锁,再一次解锁,将会导致不可预期的操作。
-
-
PTHREAD_MUTEX_RECURSIVE 递归锁(或者是嵌套锁)
这种锁允许一个线程在获得锁之后,再次申请锁,而不导致死锁。不过,当前拥有锁的线程,必须进行相应数量的解锁操作。
将锁设置为递归锁的类型之后,就不会出现死锁的情况了。
void* test_re_lock(void * args) { pthread_mutex_lock(&mutex); pthread_mutex_lock(&mutex); cout<<"test"<<endl; pthread_mutex_unlock(&mutex); pthread_mutex_unlock(&mutex); } int main() { pthread_t thread[thread_num]; // 设置线程的属性 , 设置锁的类型为递归锁 pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); pthread_mutex_init(&mutex,&attr); for(int i=0;i<thread_num;i++) { pthread_create(&thread[i],NULL,test_re_lock,NULL); } for(int i=0;i<thread_num;i++) { pthread_join(thread[i],NULL); } pthread_mutex_destroy(&mutex); cout<<"sum : "<<sum<<endl; return 0; }
-
PTHREAD_MUTEX_ERRORLOCK 默认锁
-
PTHREAD_MUTEX_DEFAULT 检错锁
-