与进程比较的优点:
1.进程的地址空房都是独立的,而线程是共享进程的内存,所以在多线程中创建一个新的线程所花费的时间要远远小于创建一个进程花费的时间,因为进程还需要系统为其分配空间。
2.由于地址空间共享,所以线程的切换速度要比进程快
3.对某些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。
一些进程函数的例子:
pthread_t pthread_self(void); (返回线程标识符)
void *f(void *arg) {
if(*(int *)arg)
printf(" 子进程所属线程 ID 为 %ld\n",pthread_self());
else
printf(" 父进程所属线程 ID 为 %ld\n",pthread_self());
pthread_exit(NULL);
}
int main()
{
pid_t pid1;
pthread_t tid1;
pthread_t tid2;
int a;
pid1 = fork();
if(pid1 == 0) {
a = 0;
printf(" 子进程主线程 ID 为 %ld\n",pthread_self());
pthread_create(&tid1,NULL,f,(void *)&a);
}
else {
a = 1;
printf(" 父进程主线程 ID 为 %ld\n",ld\n",pthread_self());
pthread_create(&tid2,NULL,f,(void *)&a);
}
}
执行程序后你会发现,子进程主线程和父进程主线程两者的ID是相同的,而子进程所属线程和父进程所属线程ID也是相同的。这是因为pthread_self() 返回的是相对于进程中各个线程之间的标识号,对于这个进程内是唯一的,而不同进程中,每个线程的 pthread_self() 可能返回是一样的。想要得到内核中真实的线程ID,需要调用syscall(__NR_gettid)函数。
1. #include <sys/syscall.h>
2. printf("The ID of this thread is: %ld\n", (long int)syscall(224));
线程同步:
当一个线程在执行调用函数时,可能还没有执行完,这次的时间片就已结束,程序调用下一个线程,而当时间片再次到来时,可能函数里的数据已经被修改,这就是线程并发访问数据时会存在的问题。为了解决这类问题,Linux系统提供了互斥锁和条件变量。
互斥锁是一个简单的锁定命令,它可以用来锁定共享资源使得其他线程无法访问。
pthread_mutex_t number_mutex;
int globalnumber;
void write()
{
pthead_mutex_lock(&number_mutex);
globalnumber++; //加锁后保证globalnumber只被修改一次,而不会多次修改。
pthread_mutex_unlock(&number_mutex);
}
void read()
{
int temp;
pthead_mutex_lock(&number_mutex);
temp = globalnumber; //加锁后保证temp可以取到正确的globalnumber的值,而不会被修改。
pthread_mutex_unlock(&number_mutex);
return(temp);
}
条件变量类似与if语句,符合条件才能执行某段程序,否则只能等待条件成立,而互斥锁会保证条件变量的值能被正确的修改。
pthread_mutex_t mutex;
pthread_cond_t cond;
void *f(void *arg) {
pthread_mutex_lock(&mutex); //由于mutex已经被加锁,程序第一次调用阻塞在这里等待解锁。调用wait解锁后函数继续执行。
pthread_cond_signal(&cond); //唤醒wait函数,使其继续运行。
pthread_exit(NULL);
}
int main()
{
pthread_t tid1;
pthread_mutex_init(&mutex,NULL);
pthread_cond_init(&cond,NULL);
pthread_mutex_lock(&mutex); //加锁
pthread_create(&tid1,NULL,f,(void *)&cond); //程序创建新的线程tid1,并调用函数f。
pthread_cond_wait(&cond,&mutex); //函数执行后,首先会将mutex解锁,然后等待cond唤醒,最后将mutex加锁
return 0;
}
上边这个程序如果不加锁,则当main函数执行到create时,tid1调用f函数发出信号,单wait函数还没有执行,所以信号被忽略,而wait执行后,接收不到信号,程序就会卡死。
线程池:线程池会提前创建一些线程,等待任务队列中的任务到达一定数量后,发出信号,线程池开始分配线程去执行这些任务。这样可以避免创建线程的时间远大于线程执行程序的时间这一问题。
下面是学长写的一个简单的线程池
组成部分:
(1) 工作线程:线程池中所有的线程
(2) 任务队列:存放所有任务函数的队列
(3) 线程池管理器 : 用于创建并管理线程池
// 任务
typedef struct workFunctor{
void *(process)(void *arg);
// 任务函数指针
void *arg;
// 函数参数
struct workFunctor;
// 指向下一个任务
}Work;
// 线程池管理器
typedef struct threadpoll{
pthread_t *pthreads;
// 工作线程列表
Work *tasks;
// 任务队列
pthread_mutex_t mutex; // 互斥锁
pthread_cond_t cond;
// 条件变量
int stop;
// 标识线程池的状态
int thread_max_num
// 线程池允许的最大活动线程
int cur_task;
// 任务队列当前任务数
}Pool;
Pool pool;
void InitPool(size_t t_num) {
pool = (Pool *)malloc(sizeof(Pool));
// 初始化互斥量和条件变量
pthread_mutex_init(&(pool-lpthread>mutex),NULL);
pthread_cond_init(&(pool-lpthread>cond),NULL);
// 初始化任务队列
pool-lpthread>tasks = NULL;
pool-lpthread>threads = (pthread_t*)malloc(sizeof(pthread_t)*t_num);
pool-lpthread>max_thread_num = t_num;
pool-lpthread>cur_tasks = 0;
pool-lpthread>stop = 0;
for(size_t i = 0;i < t_num;++i) {
pthread_create(&(pool-lpthread>threads[i]),NULL,run,NULL);
}
}
工作线程
void *run(void *arg) {
while(!pool-lpthread>stop) {
pthread_mutex_lock(&(pool-lpthread>mutex));
// 若等待队列为 0 则处于阻塞状态
while(pool-lpthread>cur_tasks == 0 && !pool-lpthread>stop) {
pthread_cond_wait(&(pool-lpthread>cond),&(pool-lpthread>mutex));
}
// 销毁线程池
if(pool-lpthread>stop) {
pthread_mutex_unlock(&(pool-lpthread>mutex));
pthread_exit(NULL);
}
// 从任务列表取出任务函数
pool-lpthread>cur_tasks-lpthread-lpthread;
Work *temp = pool-lpthread>tasks;
pool-lpthread>tasks = pool-lpthread>tasks-lpthread>next;
pthread_mutex_unlock(&(pool-lpthread>mutex));
temp-lpthread>process(temp-lpthread>arg);
// 开始执行任务
free(temp);
}
添加任务
int Append(void *(*process)(void *arg),void *arg) {
Work *newWork = (Work *)malloc(sizeof(Work));
newWork-lpthread>process = process;
newWork-lpthread>arg = arg;
newWork-lpthread>next = NULL;
pthread_mutex_lock(&(pool-lpthread>mutex));
// 将新任务添加到 tasks 的末尾
Work *p = pool-lpthread>tasks;
if(p) {
while(p-lpthread>next)
p = p-lpthread>next;
p-lpthread>next = newWork;
}
else
pool-lpthread>tasks = newWork;
pool-lpthread>cur_tasks++;
// 唤醒线程去执行此任务
pthread_cond_signal(&(pool-lpthread>cond));
pthread_mutex_unlock(&(pool-lpthread>mutex));
Return 0;
}
销毁线程池
int Desory() {
if(pool-lpthread>stop)
return -lpthread1;
pool-lpthread>stop = 1;
// 唤醒所有线程
pthread_cond_broadcast(&(pool-lpthread>cond));
// 等待所有线程结束
for(int i = 0;i < pool-lpthread>max_thread_num;++i) {
pthread_join(pool-lpthread>threads[i],NULL);
}
free(pool-lpthread>threads);
// 销毁等待队列
while(pool-lpthread>tasks) {
Work *temp = pool-lpthread>tasks;
pool-lpthread>tasks = pool-lpthread>tasks-lpthread>next;
free(temp);
}
// 销毁互斥锁和条件变量
pthread_mutex_destroy(&(pool-lpthread>mutex));
pthread_cond_destroy(&(pool-lpthread>cond));
free(pool);
}