线程的简单概述
线程也叫做轻量进程(Lightweight Process,LWP),是程序执行流的最小单元。
1.根据上面这幅图,说明一个进程的所有信息对于该进程中的线程都是共享的,包括可执行程序的代码,程序的全剧内存和堆内存,栈以及文件描述符。同时线程也包含一些私有信息。
线程标识
进程有进程ID,线程一样也有自己的ID。但是线程id只有在他所属的进程上下文中才有意义。线程ID是用pthread_t数据类型来表示的,(实现时:pthread_t是一个结构体类型)
函数1:
#include<pthread.h>
int pthread_equal(pthread_t tidl1, pthread_t tid2); //相等返回非0值,不相等返回0;
因为pthread_t类型为结构体,所以我们不能用一种可移植的方式进行打印,打印线程ID是非常有必要的
函数2:
#include<pthread.h>
pthread_t pthread_self(void);//获取线程自身ID,返回调用线程的线程ID。
线程的创建
进程当中如何新增线程?
函数3:
int pthread_create(pthread_t *restrict tidp,const pthread_attr_t *restrict attr,void *(*start_rtn)(void*),void *restrict arg);
//成功返回0,错误返回错误编号。
当pthread_create创建线程成功时,新创建的线程的ID会被设置成tidp所指向的内存单元。attr用于制定各种不同的线程属性(默认属性为NULL),新创建的线程start——rtn函数开始运行:如果需要向start_rtn中传递的参数有一个以上,那需要将这些参数放在一个结构中,然后将这个结构的地址作为arg参数传入。
示例1:如何创建线程,以及打印线程ID?
#include"../../xuexi/shell/apue.h"
#include<pthread.h>
pthread_t ntid;//全局的用于接受线程创建成功获取的信息
void printids(const char* s)
{
pid_t pid;
pthread_t tid;
pid = getpid();
tid = pthread_self();
printf("%s pid %lu tid %lu (0x%lx)\n)",s,(unsigned long)pid,(unsigned long)tid,(unsigned long)tid);
}
void* thr_fn()
{
printids("new pthread:");
return 0;
}
int main()
{
int err;//用于接受线程创建的返回值
err = pthread_create(&ntid,NULL,thr_fn,NULL);
if(err!=0)
{
err_exit(err,"can not creat pthread:");
}
printids("main pthread:");
sleep(1);
return 0;
}
线程终止
单线程可以通过三种方式退出,是在不终止整个进程的前提下,停止他的控制流。
1.线程可以简单得从启动例程中返回,返回值是线程的退出码。
2.线程可以被同一进程中的线程取消。
3.线程调用pthread_exit。
函数4:
#include<pthread.h>
int pthread_join(pthread_t thread,void **rval_ptr);
调用线程将一直阻塞,知道指定的线程调用pthread_exit,从启动例程中返回或者被取消。
如果我们对线程的返回值不感兴趣,那么可以将RVAL_PTR设置为NULL,这样调用该函数可以等待指定的线程的终止,但不获取线程的终止状态。
例2:如何获取已终止线程的退出码?
#include"../../xuexi/shell/apue.h"
//如何获取已经终止的线程的推出码
//使用了pthread_join函数
#include<pthread.h>
void* thr_fn1(void* arg)
{
printf("thread1 returning\n");
return ((void*)1);
}
void* thr_fn2(void* arg)
{
printf("thread2 exiting\n");
pthread_exit((void*)2);
}
int main()
{
pthread_t tid1,tid2;
void* tret;
int err = pthread_create(&tid1,NULL,thr_fn1,NULL);
if(err != 0)
printf("can not create thread 1");
err = pthread_create(&tid2,NULL,thr_fn2,NULL);
if(err != 0)
printf("can not create thread 2");
err = pthread_join(tid1,&tret);
if(err != 0)
printf("can not join with thread1");
printf("thread 1 exit code %ld\n",(long)tret);
err = pthread_join(tid2,&tret);
if(err != 0)
printf("can not join with thread2");
printf("thread 2 exit code %ld\n",(long)tret);
exit(0);
}
函数5:
#include<pthread.h>
int pthread_cancel(pthread_t tid);
注意这只是提出的请求,同时呢,被提出请求的线程还可以选择忽略取消或者控制如何被取消。
函数6:
#include<pthread.h>
void pthread_cleanup_push(void (*rtn)(void*),void *arg);
void pthread_cleanup_pop(int execute);
线程清理处理函数。处理程序记录在栈中,也就是说,它们的执行顺序与注册时相反。
当线程执行以下动作时,清理函数rtn是由pthread_cleanup_push函数调度的,调用时只有一个参数arg:
:调用pthread_exit时;
:响应取消请求时;
:用非零execute参数调用pthread_cleanup_pop时。
if execute参数设置为0,那么不调用清理函数。