线程

来源:互联网 发布:光纤端口一收一发 编辑:程序博客网 时间:2024/06/16 05:53

线程概念

  • 线程是在进程内部的,是进程的一个执行分支
  • linux并没有一个专门的类似于进程结构体(pcb)的一个线程描述符,它是用进程描述符来模拟线程,这样做之后,第一个进程创建了对应的pcb、虚拟地址、页表和对应的物理地址,之后在改进程内部创建的线程就和该进程共享这些东西,所以CPU在执行指令的时候并不关心那个是进程,哪个是线程。
  • CPU在各个pcb之间进程来回的切换,这样就形成了多线程
  • linux下所有的进程都可以看成是轻量级进程
  • 每次创建新进程的时候开辟对应的空间,引入线程的概念之后,进程就变成了承担分配空间的任务,而线程是调度的基本单位
  • 如果一个线程退出了,其他的线程都退出了,因为我们的所有线程共享内存资源的,所以我们的一个线程退出了之后,这个线程所开辟的虚拟地址空间就销毁了
线程和进程共享资源和独立资源
  • 共享资源有文件描述表、每种信号的处理方式(SIG_IGN、SIG_DFL或者自定义的信号处理函数)、当前工作目录、用户id和组id
  • 线程 独立资源:因为我们的线程在执行的时候需要来回的切换,这个时候我们需要保存线程的上下文信息,以便之后切换回来的时候可以在刚刚切换出去的地方继续之后,这个时候线程特有的一个资源就是上下文,包括各种寄存器的值、程序计数器和栈指针;每个线程都需要执行一个程序,那么这个程序就有变量等,所有线程就必须有自己独立的栈栈空间;此外线程还有其他的一些特有的内容:线程id,errno变量,信号屏蔽字,调度优先级。

线程创建

#include <pthread.h>       int pthread_create(pthread_t *thread, const pthread_attr_t *attr,                          void *(*start_routine) (void *), void *arg);

函数解析:
thread:这个是线程id,我们需要使用pthread_t 来定义一个线程id编程,以供pthread_create创建进程编号,并将进程 id放在这个变量里面,比如pthread_t pid;
attr:线程属性,这里我们暂时不关心,可以设置为NULL
( * start_routine ):线程执行函数,我们在执行某个线程的时候是通过让线程执行一个函数来执行该线程的,关于这个函数的设计,我们也要设计符合要求的函数,函数的返回值要是void*,函数的参数也是void*,这里我们给出一个函数的示例

void* pthread_run(void* arg){    ......    return (void*)1;}

我们的这个函数要在main函数的外面定义,我们在函数中传参的时候直接使用函数名就好了
arg:这个是前面一个函数的参数,这里也暂时设置为NULL
返回值:成功返回0,失败返回错误编号

补充:这里注意我们只要使用pthread的相关函数,在编译的选项中加上

获取线程id

#include <pthread.h>       pthread_t pthread_self(void);

和getpid的使用类似,注意这个返回值是无符号整型

线程等待

#include <pthread.h>       int pthread_join(pthread_t thread, void **retval);

参数解析
thread:线程id
retval:这个是我们需要提前定义的一个二级指针变量,比如我们定义了一个void* join,在使用的时候可以参数就是&join,这个join获取其实就是线程 函数中返回的那个值,因为返回的是void*,所以我们这里要定义一个二级指针去接受它

线程终止

#include <pthread.h>       void pthread_exit(void *retval);

参数解析
retval:这里我们可以传入的参数是(void*)12;这和使用return返回时一样的

线程取消

#include <pthread.h>       int pthread_cancel(pthread_t thread);

参数解析:
thread:线程id,我们可以使用该函数终止自己,也可以终止其他的线程,自己终止自己的时候传递的参数就是pthread_self();

线程参数传递

这里还是要回到线程创建那里,看下面的线程创建的函数

int pthread_create(pthread_t *thread, const pthread_attr_t *attr,                          void *(*start_routine) (void *), void *arg);

上面解释过,这个函数的第一个参数是一个线程id,第二个参数可以暂时设置成NULL,第三个参数是一个线程函数,这个函数的返回值是void*,参数是void*,虽然参数是void*但是不代表我们不能传递参数,pthread_create这个函数的第四个参数就可以用来给我们的线程函数传递参数使用,比如这里我们想传递一个整型变量a,但是第四个参数只能是void ,在这个时候,我们可以直接使用(void )a;进行 参数传递。然后在线程函数中,直接使用(int)arg,进行函数的使用,因为在线程函数中,那个参数只能是(void *)arg的,这个arg就是我们在使用 pthread_create时传递的第四个参数。如果我们想传递多个参数怎么办呢,这个时候我们测办法是,先定义一个结构体,然后把它强转成一个结构体的指针进行使用。

这里介绍两个指令,用于查看线程的

ps aux | grep xxx(程序名称)
ps -aL | grep xxx(程序名称)
ps -aL | head

查看进程退出结构

echo $?

查找

grep -ER ‘查找名’ …

ER表示递归查找,…表示查找的路径

附上代码

#include<stdio.h>#include<pthread.h>void* pthread_run(void* arg)   //返回值void*,参数void*{    pthread_cancel(pthread_self());    printf("进程id  :%d   线程id:%u\n",getpid(),pthread_self());    printf("i am thread!\n");    sleep(3);    //return (void*)1;    //返回值void*    pthread_exit((void*)123);  //这里的返回作用和上面的return的效果是一样的}int main(){    pthread_t pid;   //定义一个线程地址变量,供pthread_create使用    int ret = pthread_create(&pid,NULL,pthread_run,NULL);   //&pid:当pthread_create执行之后会把pid赋值,NULL线程属性,pthread_run线程函数,NULL:函数参数    //pthread_cancel(pid);    sleep(5);    void* join;    //这里的jion是我们设置的一个变量,用来记录返回的一些信息的,我们可以通过其他的一些宏函数之类的来获取保存在join里面的相关的信息    pthread_join(pid,&join);    printf("进程等待%d\n",join);    printf("进程id  :%d  线程id :%u\n",getpid(),pthread_self());    printf("%d\n",ret);    return 0;}
原创粉丝点击