Linux线程控制
Linux线程控制
1.POSIX线程库
与线程有关的函数构成了一个完整系列,绝大多数函数的名字都是以"phread_“打头的
要使用这些函数库,要通过引入同文件<pthread.h>
链接这些线程函数时要使用编译器命令的”-lpthread"选项
创建线程
#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(start_routine)(void), void *arg);
pthread_t *thread
:指向线程标识符的指针,线程创建成功后,线程标识符会被存储在这个位置。线程标识符在当前进程范围内是唯一的,用于后续对线程的操作,如等待线程结束等。const pthread_attr_t *attr
:指向线程属性结构的指针。线程属性结构可以设置线程的一些特性,如线程的分离状态、堆栈大小、优先级等。如果传入NULL
,则使用默认属性创建线程。void *(*start_routine)(void*)
:指定线程启动时要执行的函数,即线程的入口函数。这个函数必须接受一个void*
类型的参数,并返回一个void*
类型的值。线程创建后,会自动调用这个函数,并将arg
参数传递给它。void *arg
:传递给线程入口函数的参数。如果线程入口函数需要接收多个参数,可以通过将这些参数打包成一个结构体,然后将结构体的指针传递给线程入口函数。
传统的⼀些函数是,成功返回0,失败返回-1,并且对全局变量errno赋值以指⽰错误
代码示例
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <pthread.h>
void *rout(void *arg) {
int i;
for( ; ; ) {
printf("I'am thread 1\n");
sleep(1);
}
}
int main( void )
{
pthread_t tid;
int ret;
if ( (ret=pthread_create(&tid, NULL, rout, NULL)) != 0 ) {
fprintf(stderr, "pthread_create : %s\n", strerror(ret));
exit(EXIT_FAILURE);
}
int i;
for(; ; ) {
printf("I'am main thread\n");
sleep(1);
}
}
获取线程ID
#include <pthread.h>
pthread_t pthread_self(void);
打印出来的tid是通过pthread库中有函数pthread_self得到的,它返回一个pthread_t类型的变量,指代的是调用pthread_self函数的线程的"ID"。
这个ID是pthread库给每个线程定义的进程内唯一标识,是pthread库维持的。由于每个进程有自己的独立的内存空间,所以这个ID的作用域是进程级而非系统级(内核不认识)。
pthread库也是通过内核提供的系统调用(如clone)来创建线程的,而内核会为每个线程创建系统全局唯一的ID来唯一标识这个线程。
clone函数
#include <sched.h>
int clone(int (*fn)(void *), void *child_stack, int flags, void *arg, …);
pthread_self
返回的pthread_t
ID :
- 由 pthread 库分配,作用域是 进程级 。
- 用于 pthread 库内部管理和用户程序中的线程操作。
内核分配的线程 PID :
- 由内核分配,作用域是 系统级 。
- 用于内核调度和资源管理。
在实际开发中,根据需求选择合适的 ID。例如:
- 如果需要在 pthread 库中操作线程(如等待线程结束),使用
pthread_t
ID。- 如果需要与内核交互(如在日志中记录线程的唯一标识),使用线程的 PID。
LWP得到的是真正的线程ID。之前使用的pthread_self得到的这个数实际上是一个地址,在虚拟地址空间上的一个地址,通过这个地址,可以找到关于这个线程的基本信息,包括线程ID,线程栈,寄存器属性等。
在
ps -aL
得到的线程ID,有⼀个线程ID和进程ID相同,这个线程就是主线程,主线程的栈在虚拟
地址空间的栈上,⽽其他线程的栈在是在共享区(堆栈之间),因为pthread系列函数都是pthread库
提供给我们的。⽽pthread库是在共享区的。所以除了主线程之外的其他线程的栈都在共享区。
线程等待
#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);
pthread_t thread
:要等待的线程的标识符。这个标识符是通过pthread_create
函数创建线程时获得的。void **retval
:一个指向void
指针的指针,用于存储线程的返回值。如果不需要获取线程的返回值,这个参数可以设置为NULL
。
补充
图片中的内容讨论了Linux系统中线程和进程的概念,具体内容如下:
1. Linux提供的系统调用:
- 图片中提到Linux系统只提供创建轻量级进程的系统调用。这里的“轻量级进程”通常指的是用户态线程(User-Level Thread),即由用户程序和库函数管理的线程,而不是由内核直接管理的线程。
2. Linux中的线程概念:
图片中指出,在Linux系统中,实际上并不存在真正意义上的线程。这是因为Linux使用轻量级进程来模拟线程的行为。轻量级进程是一种用户态线程,它由用户程序和库函数(如pthread库)管理,而不是由内核直接管理。
在Linux中,所有的线程都是通过克隆(clone)系统调用来创建的,这些线程在内核中被视为轻量级进程。轻量级进程共享同一个进程的地址空间和其他资源,但每个轻量级进程都有自己的栈和线程控制块(Thread Control Block, TCB)。
3. 操作系统中的进程和线程:
图片中提到,在操作系统(OS)中,只有轻量级进程,所谓的模拟线程是我们的说法。这意味着在Linux中,线程是通过轻量级进程来模拟实现的,而这些轻量级进程在内核中被视为独立的进程实体。
这种模拟线程的方法允许用户程序以线程的方式进行编程,同时利用轻量级进程的高效性和灵活性。
总结来说,图片中的内容强调了Linux系统中线程是通过轻量级进程来模拟实现的,而这些轻量级进程在内核中被视为独立的进程实体。这种方法允许用户程序以线程的方式进行编程,同时利用轻量级进程的高效性和灵活性。
代码示例
#include <iostream>
#include <cstdio>
#include <string>
#include <unistd.h>
#include <pthread.h>
int flag = 100;
void showtid(pthread_t &tid)
{
printf("tid: 0x%lx\n", tid);
}
std::string FormatId(const pthread_t &tid)
{
char id[64];
snprintf(id, sizeof(id), "0x%lx", tid);
return id;
}
// code done, result ok
// code done, result not ok
// code no finish
void *routine(void *args)
{
std::string name = static_cast<const char *>(args);
pthread_t tid = pthread_self();
int cnt = 5;
while (cnt)
{
std::cout << "我是一个新线程: 我的名字是: " << name << " 我的Id是: " << FormatId(tid) << std::endl;
sleep(1);
cnt--;
flag++;
}
return (void*)123;// 暂时:线程退出的时候的退出码
}
int main()
{
pthread_t tid;
int n = pthread_create(&tid, nullptr, routine, (void *)"thread-1");
showtid(tid);
int cnt = 5;
while (cnt)
{
std::cout << "我是main线程: 我的名字是: main thread" << " 我的Id是: "
<< FormatId(pthread_self()) << ", flag: " << flag << std::endl;
sleep(1);
cnt--;
}
void *ret = nullptr; // ret也是一个变量!!也有空间哦!
// 等待的目标线程,如果异常了,整个进程都退出了,包括main线程,所以,join异常,没有意义,看也看不到!
// jion都是基于:线程健康跑完的情况,不需要处理异常信号,异常信号,是进程要处理的话题!!!
pthread_join(tid, &ret); // 为什么在join的时候,没有见到异常相关的字段呢??
std::cout << "ret is : " << (long long int)ret << std::endl;
return 0;
}