线程是什么
线程是计算机中独立运行的最小单位,运行时占用很少的系统资源,由于每个线程占用的cpu时间是由系统分配的,因此也可以把线程看成操作系统分配cpu时间的基本单位。
进程与线程的区别
相比较进程而言,线程的最大优点就是 节约,比如来说:
进程有自己独立的地址空间,而在多线程情况下,同一进程内的线程共享进程的地址空间;所以创建新线程花费时间少;
由于进程地址空间独立,而线程共享地址空间,线程间的切换速度要远快于进程间的切换速度;
创建线程
在主线程里创建线程,程序就会在创建线程的地方产生分支,变成两个程序来执行。但这和多进程一样,子进程通过拷贝父进程的地址空间来实现,而线程与进程的线程共享程序代码,一段代码可以同时被多个线程执行
创建线程的函数为pthread_creat,复习一下,创建进程的函数为系统调用fork函数;
pthread_creat函数的声明如下:
#include<pthread.h>
int pthread_creat ( pthread_t *thread, pthread_att_t *attr, void* (*start_routine) (void *),void *arg);
函数各参数含义如下:
thread 该参数是一个指针,当线程创建成功时,用来返回创建的线层id
attr 该参数用于指定线程的属性 NULL表示使用默认属性
start_rontine 该参数是一个函数指针,指向线程创建后要调用的函数,这个函数被称为线程函数
arg 该参数指向传递给线程函数的参数
当线程创建成功后,新创建的线程开始运行第三个参数所指向的函数,原来的线程继续运行
线程终止
1 通过returrn从线程函数返回
2 通过调用函数pthread_exit()使线程退出
注意两种特殊情况,一种情况是当主函数调用或者是返回了exit函数时,整个进程中止,那么进程中的所有线程肯定也会终止;还有一种就是调用函数pthread_exit,此时仅仅是主线程消亡,进程不会终止,进程中的其他线程也不会中止,直到所有线程结束,进程才会结束
现在我们放码说话:
#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
#include<unistd.h>
int *thread ( int *n )
{
*n=*n+1;
pthread_t newthid;
newthid = pthread_self();
printf ("this is a new ,threaed id =%u n=%d\n",newthid,*n);
return NULL;
}
int main()
{
int i,n=3;
pthread_t thid;
printf ("main thread ,ID is %u\n",pthread_self());// 打印主线程的id
for ( i=1; i<=3; i++ )
{
if ( pthread_create(&thid, NULL, (void *)thread,&n)!=0 )
{
printf ("thread creation failed\n");
exit(1);
}
}
// sleep(1);//主线程休眠,防止子线程来不及运行要退出
exit(0);
// pthread_exit(0);//在主函数中调用或者是返回了exit函数 整个进程都终止 进程中所有线程也会终止
// 而如果是调用pthread_exit函数 则仅仅是主线程消亡 进程不会终止 进程中的所有线程也不会中止
}
贴出上面程序的运行结果:
我们可以看到,当调用exit函数时,第一次执行时创建了一个线程,第二次和第三次则是创建了两个线程,因为当调用了exit函数后,所有线程都会终止,程序跑到哪就是哪,因为要终止整个进程;但如果当我们在exit函数前加上sleep(1) 让主线程休眠一秒,那么运行结果会是什么呢?各位看官不妨试一下
当我们注释掉exit函数,调用 pthread_exit 函数时,又会有什么结果呢?
会创建三个线程,因为 pthread_exit 函数仅仅只是让主线程消亡,其余的线程不会终止,所以程序会跑完其他线程才会终止
上面这个程序还有一个有意思的点,就是当我们调用 pthread_creat函数时,如果给函数传的为n的值,那么输出结果 n 一直是3;但如果当我们传的是n的址时,n的输出结果又会变为4 5 6,原因此篇文章就不赘述了
线程同步
说到线程同步就不能不谈谈互斥锁与条件变量了
互斥锁是一个简单的锁定命令,它可以用来锁定共享资源使得其他线程无法访问。互斥锁具有以下特点:
·原子性:把一个互斥锁定义为一个原子操作,这意味着操作系统保证了如果一个线程锁定了互斥锁,则没有其他线程可以在同一时间成功锁定这个互斥量。
·唯一性:如果一个线程锁定一个互斥量,在它接触锁定之前,没有其他线程可以锁定这个互斥量。
·非繁忙等待:如果一个线程已经锁定了一个互斥锁,第二个线程又试图去锁定这个互斥锁,则第二个线程将被挂起(不占用CPU资源),直到第一个线程解锁,第二个线程则被唤醒并继续执行,同时锁定这个互斥量。
举个栗子,两个线程操作同一临界区时,通过互斥锁保护,若A线程已经加锁,B线程再加锁时候会被阻塞,直到A释放锁,B再获得锁运行;
但这样有一个问题,线程B必须不停的主动获得锁、检查条件、释放锁、再获得锁、再检查、再释放,一直到满足运行条件的时候才可以(而此过程中其他线程一直在等待该线程的结束),这种方式是比较消耗系统资源的,所以就出现了条件变量
条件变量是对互斥锁的补充,通常条件表达式在互斥锁的保护下求值,如果条件表达式为假,那么线程基于条件变量阻塞(就不会有上述B线程的情况出现),当一个线程改变条件变量的值时,条件变量获得一个信号,使得等待条件变量的线程退出阻塞要求(上述B线程开始运行),节省资源