关于本文提到的线程操作所用到的函数以及数据结构全部在一个头文件中定义:
#include<pthread.h>
1.线程的创建和销毁:
每一个线程都有一个id,叫做句柄,一个线程创建完毕时,它的句柄也被确定下来
句柄存放在句柄变量里,就像指针存放在指针变量里一样,句柄变量的类型是pthread_t
下面的代码创建了一个用于存放句柄的数组:
pthread_t threadid[100] = {0}
开始创建线程:
pthread_create(&thread[i], NULL, thread_callback, &argc);
第一个参数是对句柄变量的引用,需要pthread_create函数能对句柄变量进行修改,函数执行成功则将创建好的线程的句柄赋值给此句柄变量
第二个参数是创建线程的属性,一般写NULL,为默认
第三个参数是该线程执行的函数,需要函数的返回类型为void*,如果不是,需要强制转换
第四个参数是传入第三个参数的参数,需要void*类型,如果有多个参数,需要用一个结构体的引用来传入这些参数
主进程结束前需要确保子线程结束并正确销毁以释放资源,否则他将成为孤儿进程或僵尸进程,这两种情况都是应该避免的
可以在主进程中等待子进程正确完成:
pthread_join(thread[i], NULL);
该函数会等待参数句柄所指代的线程完成,否则会阻塞
第一个参数是需要等待的句柄,不需要引用,第二个参数是用来获取该线程完成时的返回值,需要一个指针,线程的返回值会保存到这个指针,设置为NULL表示不需要获取该线程的返回值
2.互斥锁、自旋锁用来解决多个线程同时访问共享变量的问题,也叫做竞态条件,而出现竞态条件也算是时钟中断机制的一个缺点。
对共享变量操作的代码前后加锁解锁就可以避免竞态条件:
pthread_mutex_lock(&mutex);//加锁,参数是一个被初始化后的互斥锁的地址 (*pcount)++; // 使用互斥锁对共享变量进行保护,避免竞态条件 pthread_mutex_unlock(&mutex);//解锁,参数是一个互斥锁的地址
创建互斥锁变量:
pthread_mutex_t mutex;
其中pthread_mutex_t
是互斥锁变量的类型
之所以为变量是因为它可以具有不同的属性:
初始化互斥锁变量:
pthread_mutex_inint(&mutex, NULL);
第一个参数是对一个互斥锁变量的引用
第二个参数规定了该互斥锁的属性,默认为NULL
互斥锁的生命周期:创建-> 初始化-> 被引用-> 被销毁
需要注意的是,使用未初始化的互斥锁变量可能会访问位置内存,造成崩溃,程序结束前未销毁互斥锁变量可能会造成资源泄露
互斥锁的销毁:
pthread_mutex_destory(&mutex);
对于自旋锁,操作上与互斥锁无区别,只需要把mutex字段改成spin
cas和原子操作另作文章
3.条件变量:
pthread_cond_t cond;
pthread_cond_t 是条件变量的类型, 用来声明一个条件变量,使用前需要初始化:
pthraed_cond_init(&cond, NULL)
第二个参数代表条件变量的属性,默认NULL
先看一个例子:
static void *thread_pool_callback(void *arg) { struct workers *worker = (struct workers*)arg; while (1) { pthread_mutex_lock(&worker->manager->mutex); while (worker->manager->tasks == NULL) { if (worker->terminate) break; pthread_cond_wait(&worker->manager->cond, &worker->manager->mutex); if (worker->terminate) break; } if (worker->terminate) { pthread_mutex_unlock(&worker->manager->mutex); break; } struct tasks *task = worker->manager->tasks; LIST_DELETE(task, worker->manager->tasks); pthread_mutex_unlock(&worker->manager->mutex); task->task_func(task); } free(worker); return NULL; }
程序会阻塞到pthread_cond_wait(&cond,&mutex)
直到收到信号将它唤醒
而这个信号需要有函数来发送:
1.pthread_cond_signal(&cond)
:唤醒一个正在条件变量cond上等待的线程,如果有多个线程在等待,具体唤醒哪一个线程我们无从得知,取决于系统的调度策略
2.pthread_cond_broadcast(&cond)
:唤醒所有在cond上等待的线程
注意:使用信号来唤醒等待的线程时,wait和signal、broadcast必须使用同一个条件变量cond
相信你也注意到了,pthread_cond_wait(&cond, &mutex);
的第二个参数是一个互斥锁变量,这是用来确保wait操作的原子性,也就是说pthread_cond_wait(&cond, &mutex);
前后必须上锁、解锁
最后销毁条件变量:
pthread_cond_destory(&cond);
注意:在尝试销毁处于等待状态的条件变量时,会失败,不能过河拆桥!