线程概念:
线程:是进程内的一个执行分支。线程的执行颗粒度,比进程要细。
1.在Linux中,线程在进程的"内部"执行,线程在进程的地址空间内运行。
2.在Linux中,线程的执行粒度要比进程更细,因为线程执行的是进程代码的一部分。
(在Linux中没有真正意义上的线程,而是用"进程"模拟的线程。复用进程数据结构和管理算法)
CPU:线程 <= 执行流 <= 进程
重新定义进程和线程
什么叫做线程? 线程是操作系统调度的基本单位
重新理解进程?内核观点,进程是承担分配系统资源的基本实体
线程是进程内部的执行流资源。
进程是资源分配的基本单位。
线程是调度的基本单位。
线程共享进程数据,但也拥有自己的一部分数据:
1.线程ID
2.一组寄存器
3.栈
4.errno
5.信号屏蔽字
6.调度优先级
线程比进程要更轻量化(为什么?):
a.创建和释放更加轻量化
b.切换更加轻量化
(整个生命周期,cpu中cache缓冲的热数据,线程内的切换,不需要cache数据)
POSIX线程库
与线程有关的函数构成了一个完整的系列,绝大多数函数的名字都是以“pthread_”打头的
要使用这些函数库,要通过引入头文<pthread.h>
链接这些线程函数库时要使用编译器命令的“-lpthread”选项
创建一个新线程:
int pthread_create(pthread_t* thread, const pthread_attr_t* attr, void (start_routine)(void), void arg);
pthread_t(无符号长整数):输出型参数,用于存储新创建线程的线程 ID。
pthread_attr_t:线程的属性,设置为nullptr,则使用默认属性
void *(start_routine)(void):函数指针
arg:指向将在创建新线程时传递给 start_routine 函数的参数。( 创建线程成功,新线程回调线程函数的时候,需要参数,这个参数就是给线程函数传递的)
#include <iostream>
#include <pthread.h>
void* Test(void* args)
{
std::cout << "hello pthread" << std::endl;
}
int mian()
{
pthread_t td;
pthread_create(&td,nullptr,Test,nullptr);
return 0;
}
用于等待指定线程的结束
int pthread_join(pthread_t thread,void** retval);
thread:要等待的线程的标识符。
retval:用于存储被等待线程的返回值。如果不关心返回值,可以传递 NULL。
如果成功,返回 0。
如果出错,返回错误码。
#include <iostream>
#include <pthread.h>
void* Test(void* args)
{
std::cout << "hello pthread" << std::endl;
}
int mian()
{
pthread_t td;
pthread_create(&td,nullptr,Test,nullptr);
pthread_join(td,nullptr); //不join线程,主线程退出,线程也会退出
return 0;
}
终止调用线程,并返回一个指定的值
void pthread_exit(void* retval);
#include <pthread.h>
#include <stdio.h>
void* thread_func(void* arg) {
int thread_id = *(int*)arg;
printf("Thread %d is running.\n", thread_id);
// 终止线程并返回一个值
int result = thread_id * 10;
pthread_exit((void*)&result);
}
int main() {
pthread_t thread;
int thread_id = 123;
// 创建线程
pthread_create(&thread, NULL, thread_func, (void*)&thread_id);
// 等待线程结束并获取返回值
void* thread_result;
pthread_join(thread, &thread_result);
int* result_ptr = (int*)thread_result;
printf("Thread result: %d\n", *result_ptr);
return 0;
}
获取当前线程的线程 ID
pthread_t pthread_self(void);
请求取消指定线程
int pthread_cancel(pthread_t thread);
pthread_cancel函数用于向指定的线程发送取消请求。当调用该函数后,目标线程将收到一个取消请求,并根据其设置的取消状态进行响应。
返回值为0表示成功发送取消请求,非0值表示失败。
#include <iostream>
#include <pthread.h>
void* Test(void* args)
{
while(true)
{
std::cout << "hello pthread" << std::endl;
pthread_cancel(pthread_self());
}
}
int mian()
{
pthread_t td;
pthread_create(&td,nullptr,Test,nullptr);
pthread_join(td,nullptr); //不join线程,主线程退出,线程也会退出
return 0;
}
CLONE
clone 系统调用是 Linux 提供的一个用于创建新进程的系统调用
int clone(int (*fn)(void *), void child_stack, int flags, void arg, …);
fn:一个函数指针,指向新进程或线程的入口函数。
child_stack:子进程或线程的栈顶指针。对于进程,它指向新进程的用户栈顶;对于线程,它指向新线程的栈顶。栈通常需要提前分配并 映射到合适的内存区域。
flags:创建进程或线程的标志参数。
arg:传递给新进程或线程入口函数的参数。
…:可变参数列表,用于传递其他参数给入口函数。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sched.h>
int thread_func(void* arg) {
int thread_id = *(int*)arg;
printf("Thread %d is running.\n", thread_id);
// 模拟线程执行
for (int i = 0; i < 5; i++) {
printf("Thread %d: %d\n", thread_id, i);
sleep(1);
}
printf("Thread %d is finished.\n", thread_id);
return 0;
}
int main() {
int thread_id = 123;
void* child_stack = malloc(16384); // 分配子线程栈空间,大小为16KB
// 使用clone创建新线程
int clone_flags = CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_THREAD;
int thread = clone(thread_func, (char*)child_stack + 16384, clone_flags, &thread_id);
if (thread == -1) {
perror("Failed to create thread");
return 1;
}
printf("Main thread is running.\n");
// 等待一段时间
sleep(3);
// 终止子线程
if (kill(thread, SIGKILL) == -1) {
perror("Failed to kill thread");
return 1;
}
// 等待子线程结束
if (waitpid(thread, NULL, 0) == -1) {
perror("Failed to wait for thread");
return 1;
}
printf("Main thread is finished.\n");
free(child_stack);
return 0;
}
线程分离
int pthread_detach(pthread_t thread);
成功返回 0;失败时,返回一个非零的错误码
用于将指定的线程标记为可被分离的。分离一个线程意味着当线程退出时,其资源(如线程的存 储空间)会被自动释放,而不需要其他线程调
#include <iostream>
#include <pthread.h>
void* Test(void* args)
{
pthread_detach(pthread_self());
while(true)
{
std::cout << "hello pthread" << std::endl;
}
}
int mian()
{
pthread_t td;
pthread_create(&td,nullptr,Test,nullptr);
return 0;
}
线程ID及进程地址空间布局
pthread_ create函数会产生一个线程ID,存放在第一个参数指向的地址中。该线程ID和前面说的线程ID
不是一回事。
前面讲的线程ID属于进程调度的范畴。因为线程是轻量级进程,是操作系统调度器的最小单位,所以需要
一个数值来唯一表示该线程。
pthread_ create函数第一个参数指向一个虚拟内存单元,该内存单元的地址即为新创建线程的线程ID,
属于NPTL线程库的范畴。线程库的后续操作,就是根据该线程ID来操作线程的。