Linux-线程概念-线程控制-进程和线程对比-线程创建教程
文章目录
1. 线程概念
1.一个进程当中一定存在一个主线程,执行main函数的线程就称为主线程。
2.其他线程都被称为工作线程。
3.之前我们说到的进程,本质上是线程组,换句话说,线程组被称为进程。
4.线程也可以称之为轻量级进程(LWP),操作系统内核当中,压根是没有线程概念的。
5.
pid_t pid;
pid_t tgid;
pid:轻量级进程id,也被称之为线程id
tgid:轻量级进程组id,也被称之为进程id
6.在一个进程当中,不管这个进程由多少个线程,在所有的线程的PCB当中,tgid都是相等的。
7.主线程(执行main函数的LWP)的pid和tgid相等。
8.处理主线程,工作线程的pid,都是不一样的,可以用pid去区分到底是哪一个线程。
线程的共享与独有:
独有: 在共享区当中由自己的调用堆栈,寄存器,线程ID,errno,信号屏蔽字,调度优先级(PR)。
共享: 文件描述符(fd\_array[xxx]),当前进程工作目录,用户id和用户id,信号的处理方式。
并发:多个执行流在同一时刻只能有一个执行流拥有cpu进行运算。
线程的优点:
1.一个进程当中多个执行流可以并行的执行代码,就可以提高程序的运行效率。
2.进程切换要比线程切换操作系统付出的代价大。
3.线程占用的资源要比进程少很多
4.可并行的运行
线程的缺点:
1.当一个进程当中,线程数量远远大于cpu数量的时候,就有可能线程切换的开销影响线程的效率,因此,程序当中的线程数量不是越多越好,这是有一个阈值的,想要得到阈值就要进行压力测试,这块可能就会牵扯到高并发。
2.健壮性降低,健壮性和鲁棒性都是描述代码运行稳定的词语。
3.缺乏访问控制
4.编写难度高,主线程和工作线程的时序性不好控制。
总结:线程(LWP)是操作系统调度的基本单位,进程是操作系统资源分配的基本单位。
2. 线程控制
2.1 进程和线程的对比
1.进程的健壮性要比线程好,多进程的应用场景:shell,守护进程,分布式服务,守护进程和被守护的进程之间用的是共享内存,共享内存当中给守护进程传递时间,如果几次传递的时间都是没有发生改变的,那么就说明子进程挂掉了,为了24小时不间断服务,守护进程会杀死子进程,然后重新拉起来一个子进程,但是就算是这样,也并不能保证24小时不间断服务,因此还存在一个分布式服务,会存在多个服务端。
这里就涉及到了nginx中的负载均衡和路由转发,负载均衡是说会根据服务器性能的好坏,对不同的服务器按一定的比例转发,路由转发是负则用户将消息给对应的服务器发送,如果某一个服务器挂掉了,就默认不会给那个服务器发送。
2.多线程要比多进程耗费资源小,并且切换快,程序运行效率高。
2.2 线程创建
接口:
int pthread\_create(pthread\_t *thread, const pthread\_attr\_t *attr, void (start\_routine) (void ), void arg);
pthread\_t:线程的标识符,本质上是一个线程在共享区独有空间的首地址
thread:是一个出参,该值是由pthread\_create函数赋值的
pthread\_attr\_t:创建线程的属性,一般情况下指定为NULL,采用默认属性
void (start\_routine)(void)
函数指针:接受一个返回值为void,参数为void的函数地址,就是线程的入口函数(相当于程序的主函数)
void* arg:给线程入口函数传递的参数
由于参数的类型是void*,所以给了程序无限传递参数的方式。
char,int,结构体指针,this指针
返回值:
失败:<0
成功:=0
多线程程序的makefile文件和普通的程序编译文件是不一样的,如果用普通的编译的方法,会出现下面这种情况。
在编译多线程程序的时候,一定要链接libpthread.so文件 -lpthread
命令: g++ thread\_create.cpp -o thread\_create -lpthread
1.主线程和工作线程都是死循环
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
void* MyThreadStart(void* arg)
{
while(1)
{
printf("i am MyThreadStart\n");
sleep(1);
}
}
int main()
{
pthread_t tid;
int ret = pthread_create(&tid, NULL, MyThreadStart, NULL);
if(ret < 0)
{
perror("pthread_create fail");
return 0;
}
while(1)
{
printf("i am main\n");
sleep(1);
}
return 0;
}
通过调用堆栈验证,可以查看主线程和工作线程,有调用main的是主线程,另一个是工作线程,或者根据线程号,进程号和线程号相同的为主线程,其余均为工作线程。
之后尝试把主线程中的循环去掉,也就是说主线程退出,工作线程不退出,查看进程的情况,结果是进程结束,也就是说主线程退出后,工作线程也会退出。
命令: top -H -p 20022(pid)
功能: 可以查看进程中的线程执行情况
2.工作进程死循环,将主线程中的循环去掉
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
void* MyThreadStart(void* arg)
{
while(1)
{
printf("i am MyThreadStart\n");
sleep(1);
}
}
int main()
{
pthread_t tid;
int ret = pthread_create(&tid, NULL, MyThreadStart, NULL);
if(ret < 0)
{
perror("pthread_create fail");
return 0;
}
return 0;
}
3.传递一个i=1,2,3,4
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
struct ThreadId
{
int thread_id_;
};
void* MyThreadStart(void* arg)
{
struct ThreadId* ti = (struct ThreadId*)arg;
while(1)
{
printf("i am MyThreadStart, i = %d\n", ti->thread_id_);
sleep(1);
}
delete ti;
}
int main()
{
pthread_t tid;
for(int i = 0; i < 4; i++)
{
struct ThreadId* ti = new ThreadId();
ti->thread_id_ = i;
int ret = pthread_create(&tid, NULL, MyThreadStart, (void*)ti);
if(ret < 0)
{
perror("pthread_create fail");
return 0;
}
}
//create success
while(1)
{
printf("i am main thread\n");
sleep(1);
}
return 0;
}
正常来说for循环是按照1234的顺序传递的,打印出来应该也是1234,但是,由于线程之间是抢占式的执行方式,所以输出的顺序可能不同。