本文导航:
内容所占比率
线程概念
40%
线程与进程区别与联系
20%
线程异同点
10%
线程控制(创建,中止,等待)
30%
线程的概念
提到线程,我们先从进程说起。
我们写的程序从硬碟加载到显存开始运行时,进程就形成了。也就是操作系统开始为这个程序创建PCB,分配系统资源,例如分配一块虚拟地址空间,一个页表,一块化学显存。当这个进程内部有多个执行流时,如今我们可以简单理解父进程用vfork()创建了多个子进程时linux下socket编程,这种子进程也须要资源。倘若能分得资源都会只身分的资源,但若果分不了就共享。共享一块地址空间。共享一个页表和一块化学显存。
为了易于来理解,我画了一张简明图
在这张图中,有3个PCB,在原先的认知里,我们称作3个进程。
如今我们须要引入线程的概念。
线程概念
进程到线程
在明晰线程概念后,我们就要站在线程的角度去理解前面那张图了。
图中3个PCB,我们就要把她们称之为3个线程了。由于这是在进程里的3个执行控制流。她们共享一块虚拟地址空间。一块页表,所以这3个线程所见到的化学类存也是一样的。并且她们可以执行各自的任务linux线程和进程的区别,并有自己生命特点。
所以在图中,进程就是创建执行代码,创建PCB,申请资源,分配资源的实体,即整个图。而线程就是这些单个PCB,在线程中称之为TCB。
其实下边这张图有助于来理解。
线程特征
其中最重要的数据是栈和寄存器。私有栈是为了保存临时变量linux线程和进程的区别,以便函数调用等操作。私有寄存器是为了便捷线程切换,保存上下文。
进程的多个线程共享
.同地址空间,因而TestSegment,DataSegment都是共享的,假如定义个函数雇各线程中都可以调用,假如定义一个全局变量,在各线程中都可以访问到,除此之外,各线程还共享以下进程资源和环境:
进程与线程联系
在Linux下并没有专门为线程设计如此一个概念,也就是说没有真正意义上的线程,它在linux下是由进程模拟的,可以觉得所有的PCB都可以称为轻量级进程(不一定是进程,也可能是线程)。进程是分配系统资源的一个实体linux端口映射,而线程是CPU调度的基本单位。
在单个程序中同时运行多个线程完成不同的工作,称为多线程。相对来说,多进程相对稳定,多线程相对不稳定。可以觉得,进程是分配系统资源的一个实体,而线程是CPU调度的基本单位。
进程具有独立的地址空间,而线程并没有,同一进程内部的线程共享进程的地址空间。
因为Linx下没有真正的线程,Linux用进程来描述线程和组织线程。所以在Linux下只有轻量级的线程,操作系统是看不到线程的TCB的。所以右图中的第四个图:多个进程多个线程图中,操作系统可以看见的是6个PCB,
线程的优点线程的缺点线程控制POSIX线程库
与线程有关的函数构成了一个完整的系列,绝⼤大多数函数的名子都是以“pthread_”打头的
要使用这种函数库,要通过引入头文件,链接这种线程函数库时要使用编译器命令的“-lpthread”选项
常见操作如下
创建线程
调用函数
返回值:成功返回0,失败返回错误码
错误检测:
传统的一些函数是,成功返回0,失败返回-1,但是对全局变量errno形参以指示错误。
pthreads函数出错时不会设置全局变量errno(而大部份其他POSIX函数会这样做)。而是将错误代码通过返回值返回pthreads同样也提供了线程内的errno变量,以支持其它使用errno的代码。对于pthreads函数的错误,建议通过返回值业判断,由于读取返回值要比读取线程内的errno变量的开支更小
测试用例
注意:编译链接时,一定要加上-lpthreead选项,见图中命令部份。由于这是用户级别的库,并不是系统提供的库。
#include
#include
#include
//新线程的例程(执行任务,打印字符串)
void * my_run( void *arg)
{
while(1)
{
printf( " i am %sn",( char *)arg);
sleep( 1);
}
}
int main( )
{
//申明一个本地变量tid,用来保存对等线程的ID
pthread_t tid;
//调用pthread_create函数创建一个新的线程,
pthread_create(&tid,NULL,my_run,"newthread");
//调用结束,同时运行,并且tid包含了新线程的id
while(1)
{
printf( " i am main threadn");
sleep(2);
}
return 0;
}
pthread_create创建成功!
主线程和新线程各自复印不同的内容。
正常情况下,一个程序里是不会同时执行两个死循环的。并且,在这个程序里,竟然可以同时执行两个死循环。缘由就在于这时两个线程在运行。各自执行不同的任务,有自己的栈空间和寄存器,支持上下文切换和函数调用。
中止线程
假如须要只中止某个线程而不中止整个进程,可以有以下方式:
void * my_run( void *arg)
{
while(1)

{
printf( " i am %sn",( char *)arg);
sleep( 1);
return NULL;
}
}
新线程中止,主线程继续执行。
-线程可以调用pthread_exit中止自己。
pthreadexit函数
功能:线程中止
#include
void pthread_exit(void *retval);
参数
valueptr:valueptr不要指向一个局部变量。
返回值:无返回值,跟进程一样,线程结束的时侯难以返回到它的调用者(自身)
#include
#include
#include
#include
void * my_run( void *arg)
{
printf("thread 1 returning ....n");
int *p=(int *)malloc(sizeof(int));
*p=1;
pthread_exit((void *)p);
}
int main( )
{
pthread_t tid;
void *ret;
//thread 1 exit
pthread_create(&tid,NULL,my_run,NULL);
//等待新线程退出,退出码保存在ret中
pthread_join(tid,&ret);
printf("thread return,id is:%ld,return code:%dn",tid,*(int *)ret);

free(ret);
while(1)
{
printf("i am main threadn");
sleep(2);
}
return 0;
}
,pthreadexit或则return返回的表针所指向的显存单元必须是全局的或则是用malloc分配的,不能在线程函数的栈上分配,由于当其它线程得到这个返回表针时线程函数早已退出了。
调用phread_exit(),他会等待所有其他对等线程(就是同一个进程中的除自身外其他线程)中止,之后再中止主线程和整个进程。
假如某个对等线程调用linux的exit函数,则该函数中止进程以及所有与该进程相关的线程
pthread_cancel函数
功能:取消一个执行中的线程
#include
int pthread_cancel(pthread_t thread);
参数
thread:线程ID
返回值:成功返回0;失败返回错误码
void* my_run_2(void* arg)
{
while(1)
{
printf( "thread 2 is running...n");
sleep(1);
}
return NULL;
}
int main( )
{
pthread_t tid;
void *ret;
pthread_create(&tid,NULL,my_run_2,NULL);
sleep(1);
//取消执行中的线程2
pthread_cancel(tid);
//等待线程2的返回状况,获取返回值
pthread_join(tid,&ret);
if(ret==PTHREAD_CANCELED)

{
printf("thread return,id is:%ld,return code:PTHREAD_CANCELEDn",tid);
}
else
{
printf("thread return,id is:%ld,return code:NULLn",tid);
}
return 0;
}
介绍一个获取当前线程id的函数
#include
pthread_t pthread_self(void);
获取当前文件中线程tid命令
ps -eLf | head -1&& ps -eLf | grep a.out
以上都是显示中止,另外还有隐式中止。即:顶楼的线程调用返回时,线程会隐式中止。
线程等待
为何须要线程等待?
早已退出的线程,其空间没有被释放,一直在进程的地址空间内。创建新的线程不会复⽤用刚刚退出线程的地址空间。
功能:等待线程结束原型
intpthread_join(pthread_tthread,void**valueptr);
参数
thread:线程ID
value_ptr:它指向一个表针,前者指向线程的返回值返回值:成功返回0;失败返回错误码
调用该函数的线程将挂起等待,直至id为thread的线程中止。thread线程以不同的方式中止,通过pthread_join得到的中止状态是不同的,总结如下:
假如thread线程通过return返回,valueptr所指向的单元⾥里储存的是thread线程函数的返回值。
假如thread线程被别的线程调⽤用pthreadcancel异常终掉,value_ptr所指向的单元里储存的是常数
PTHREADCANCELED。
假如thread线程是自调用pthreadexit中止的,valueptr所指向的单元储存的是传给pthread_exit的参数。
假如对thread线程的中止状态不感兴趣,可以传NULL给value_ptr参数。
测试用例可以参考前面的代码。
线程分离
线程分离插口
#include
int pthread_detach(pthread_t thread);
#include
int pthread_detach(pthread_t self());
返回值:都是成功返回0,失败返回-1.
线程被分离后,就不能在进行pthread_join()操作。否则会出错。由于分离后的线程资源手动就被回收了,再进行等待回收资源。必将造成等待失败。
测试用例如下
#include
#include
#include
#include
void * thread_run( void *arg)
{
//新分离线程自我分离
pthread_detach(pthread_self( ));
printf("%sn",( char *)arg);
return NULL;
}
int main( )
{
pthread_t tid;
if( pthread_create(&tid,NULL,thread_run,"thread1 run...")!=0)
{
printf("create thread errorn");
return 1;
}
//主线程分离新线程
//pthread_detach(tid);
int ret=0;
sleep(1);
if( pthread_join( tid,NULL)==0)
{
printf("wait thread successn");
ret=0;
}
else
{
printf("wait thread failedn");
ret= 1;
}
return ret;
}