目录
前言
提示:这里可以添加本文要记录的大概内容:
我们知道并发是轮询进行的,而线程就是实现并发处理的。下面的代码都是同一个代码,除去第一次出现的代码是完整的,下面出现一部分是说明这一部分改了一下,没有出现的说明该代码没有被修改。下面有的线程写成进程了,担待担待
提示:以下是本篇文章正文内容,下面案例可供参考
一、认识线程
线程的概念:
线程是操作系统中能够进行运算调度的最小单位,是进程中的一条执行流。通俗来讲,
进程的工厂,线程是工人,工人有很多岗位可以做不同的事情,这些工人组合一起工厂
才能运行。
在Linux系统中,执行流是通过pcb实现的,进程中的线程共享了进程中的大部分资源,相较于传统的pcb较为轻量化,也被称之为轻量化进程。
线程间的独有和共享
独有:每个线程所独有的数据-每个线程自己的独立副本,互不干扰。
如:标识符、栈、上下文数据、信号屏蔽字、优先级等
共享:进程中的pcb共享资源-多个线程可以访问的资源
如:虚拟地址空间、IO信息、信号处理方式、工作路径等
注意点:一个进程至少要有一个线程能够进行任务处理。 能用于进程间的通信方式也能用于线程
二、控制线程
对线程的操作有三点:创建、终止、等待与分离。由于系统并没有直接用于进行线程控制的系统调用接口,所以用于实现线程控制的大多都是对系统调用接口进行封装而来的。
1.创建
接口:
1、 int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *
(*start_routine) (void *), void *arg); //用于创建一个新的线程
参数:
pthread_t *thread:是一个指向pthread_t类型变量的指针,用于存储被创建出的线程ID
const pthread_attr_t *attr:用于指定线程的属性,通常置NULL
void *(*start_routine) (void *):传入一个函数地址,这个地址是线程要运行的函数。这
个函数必须返回void*并且接收的参数是void*类型的参数
void *arg:给系统函数设置的参数,可以传递任何类型的数据
返回值:成功返回0,失败返回错误码
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
void* prin(void* arg){
while(1){
printf("创建进程成功,并且传入的数据是:%s\n", (char*)arg);
sleep(1);
}
return NULL;
}
int main(){
pthread_t thread; //创建一个用于存储进程ID的变量
char* data = "2024-10-25";
//创建一个进程
if(pthread_create(&thread, NULL, prin, (void*)data) != 0){
printf("创建线程失败");
return -1;
}
while(1){
printf("cpu正常调度的函数\n");
sleep(1);
}
return 0;
}
2.终止
终止就是:如何退出线程
我们首先知道,一个程序的最小调度单位是函数,而一个进程的最小执行流是线程。在上面我们创建线程要运行的函数void*prin(),这是线程的入口函数。
1、每个线程都有一个如何函数,当这个函数运行到return的时候就退出运行的。
2、当进程退出的时候,里面的所有线程也全部退出。
3、main函数中的ruturn退出的是进程。
4、void pthread_exit(void* retval)退出线程的函数,放入我们的自定义线程中就会自动退出。但若放入主线程中,主线程会退出而子线程不会退出,也就是进入了僵尸状态。重点普通线程退出之后,为了能够保存返回值,用户态的空间并没有被回收释放,而是需要被其他线程等待获取返回值之后才会释放。和进程的等待类似。
5、int pthread_cancel(pthread_t tid); 这个函数是取消指定线程的运行,并且会退出返回值。
void* prin(void* arg){
while(1){
printf("创建进程成功,并且传入的数据是:%s\n", (char*)arg);
sleep(5);
pthread_exit(NULL); 退出线程
}
return NULL;
}
int main(){
pthread_t thread; //创建一个用于存储进程ID的变量
char* data = "2024-10-25";
//创建一个进程
if(pthread_create(&thread, NULL, prin, (void*)data) != 0){
printf("创建线程失败");
return -1;
}
while(1){
printf("cpu正常调度的函数\n");
sleep(1);
pthread_cancel(thread);
}
return 0;
}
3.等待与分离
线程等待:等待一个线程退出,获取它的返回值,释放它的全部资源。一个线程被创建出来
之后,默认有一个分离属性,值为joinable,处于joinable状态的线程退出的时
候,不会自动释放资源,需要获取返回值在释放资源。当一个我们需要等待线程
退出,和想要获取返回值的时候,用线程等待。
接口:int pthread_join(pthread_t thread, void **retval); 阻塞接口,等待线程没有退出
会一直等待。
**retval:指向指针的指针,用于保存返回值,返回值未一级指针,如果不想
要返回值可以置NULL;
线程分离:将默认是分离属性设置为detach,分离属性处于detach状态的线程,退出后会自
动释放所有资源。此时的线程是不需要被等待的,等待也不一定能获取返回值。
当我们不需要等待线程的退出,并且不需要返回值,就可以用线程分离
接口:int pthread_detach(pthread_t thread); 仅仅用于设置分离属性
void* prin(void* arg){
int sum = 3;
while(1){
printf("创建进程成功,并且传入的数据是:%s\n", (char*)arg);
sleep(1);
sum--;
if(sum == 0){
pthread_exit(NULL);
}
}
return NULL;
}
int main(){
pthread_t thread; //创建一个用于存储进程ID的变量
char* data = "2024-10-25";
//创建一个进程
if(pthread_create(&thread, NULL, prin, (void*)data) != 0){
printf("创建线程失败");
return -1;
}
pthread_join(thread, NULL);
printf("等待完毕退出线程\n");
while(1){
printf("cpu正常调度的函数\n");
sleep(1);
pthread_cancel(thread);
}
return 0;
}
int main(){
pthread_t thread; //创建一个用于存储进程ID的变量
char* data = "2024-10-25";
//创建一个进程
if(pthread_create(&thread, NULL, prin, (void*)data) != 0){
printf("创建线程失败");
return -1;
}
// pthread_join(thread, NULL);
pthread_detach(thread);
while(1){
printf("cpu正常调度的函数\n");
sleep(1);
pthread_cancel(thread);
}
return 0;
}
cup同时调度两个线程,当遇见分离属性接口的时候直接改变分离属性,退出线程