欢迎来到Cefler的博客😁
🕌博客主页:折纸花满衣
🏠个人专栏:题目解析
目录
👉🏻pthread_t类型
对于Linux目前实现的NPTL实现而言,pthread_t类型的线程ID,本质就是一个进程地址空间上的一个地址。
👉🏻线程终止
如果需要只终止某个线程而不终止整个进程,可以有三种方法:
- 从线程函数return。这种方法对主线程不适用,从main函数return相当于调用exit。
- 线程可以调用
pthread_ exit
终止自己。 - 一个线程可以调用
pthread_ cancel
终止同一进程中的另一个线程
pthread_exit函数
pthread_exit
函数是POSIX标准线程库(pthread)中的一个函数,用于终止当前线程并返回一个值。
函数原型
c
void pthread_exit(void *value_ptr);
参数
value_ptr
:表示线程的退出状态,可以是任意类型的指针,用于向线程池或其他线程传递信息。
功能
- 当线程调用
pthread_exit
函数时,它会立即终止当前线程的执行,并将value_ptr
指向的值传递给等待该线程的其他线程。 - 如果线程没有调用
pthread_exit
而是从线程函数中返回,那么该线程的返回值将会是PTHREAD_CANCELED
,表示线程被取消。 pthread_exit
函数不会关闭调用它的线程,只会终止该线程的执行 。如果在线程函数中调用pthread_exit
,它将终止该线程的执行,但不会影响其他线程的执行。
注意事项
- 线程在调用
pthread_exit
退出后,其资源(如内存)会被释放。 - 在多线程编程中,通常会使用
pthread_join
函数来等待线程结束,并获取线程的退出状态。
总之,pthread_exit
函数用于在线程中提前终止当前线程的执行,并可以传递一个退出状态给其他线程。
pthread_cancel函数
pthread_cancel
函数是POSIX标准线程库(pthread)中的一个函数,用于请求取消指定线程的执行。
函数原型
c
int pthread_cancel(pthread_t thread);
参数
thread
:要取消的线程的标识符。
功能
- 当调用
pthread_cancel
函数时,会向指定的线程发送取消请求。 - 被取消的线程将在适当的时候(通常是在取消点)终止执行。
- 线程可以在取消请求到来时选择如何响应,可以忽略取消请求或执行相应的清理工作后退出。
返回值
- 如果成功 发送取消请求,则返回0;否则返回一个非零值。
注意事项
- 使用
pthread_cancel
函数并不一定会立即终止目标线程的执行,线程需要在合适的时机进行检查才能响应取消请求。 - 取消请求只是一个请求,线程可以选择忽略它。
- 在使用
pthread_cancel
时,需要确保目标线程处于可取消状态,可以通过设置线程属性来控制线程的可取消状态。
总之,pthread_cancel
函数用于向指定线程发送取消请求,被取消的线程在适当的时机响应取消请求并终止执行。
👉🏻线程等待
在线程编程中,线程等待是指一个线程暂停执行,直到满足特定条件或事件发生时再继续执行。线程等待通常用于协调多个线程之间的执行顺序和数据共享,以确保线程之间的正确同步和协作。
🌈 为什么需要线程等待?
-
同步操作 :在线程之间需要进行同步操作时,其中一个线程可能需要等待另一个线程完成特定任务后才能继续执行,以避免竞争条件或数据不一致的情况。
-
资源共享:多个线程可能需要共享资源,但在某一时刻只能有一个线程访问该资源,其他线程需要等待资源可用时再进行访问,以避免冲突和数据损坏。
-
任务协作:在线程间存在任务依赖关系时,某些线程需要等待其他线程完成特定任务后才能继续执行,以确保整个任务流程正确顺序执行。
-
阻塞等待:某些线程需要等待外部事件的发生,如I/O操作完成、定时器到期等,以避免忙等浪费CPU资源。
🌈 线程等待的实现方式
-
条件变量 :使用条件变量机制可以让线程在特定条件下等待或被唤醒,例如
pthread_cond_wait
和pthread_cond_signal
函数。 -
互斥锁 :线程可以使用互斥锁来保护共享资源,当资源处于不可用状态时,线程可以通过尝试获取锁而进入等待状态。
-
信号量:信号量机制也可以用于线程等待,通过控制信号量的值来实现线程的阻塞和唤醒。
-
线程join :主线程可以通过调用
pthread_join
函数等待其他线程的结束,以确保在子线程执行完成后再继续执行。
线程等待是多线程编程中重要的同步手段,能够有效地解决多线程并发执行中的竞争条件、数据一致性和执行顺序等问题,提高程序的正确性和可靠性。
pthread_join函数
pthread_join
函数是POSIX标准线程库(pthread)中的一个函数,用于等待指定线程的结束,并获取该线程的退出状态。
函数原型
c
int pthread_join(pthread_t thread, void **status);
参数
thread
:要等待结束的线程的标识符。status
:用于获取目标线程的退出状态的指针。
功能
- 当调用
pthread_join
函数时,当前线程将会阻塞,直到指定的线程结束执行。 - 如果目标线程已经结束,那么
pthread_join
函数会立即返回;否则将一直等待,直到目标线程结束。 - 通过
status
参数可以获取目标线程的退出状态,即线程函数的返回值或调用pthread_exit
时传递的值。
返回值
- 如果成功 等待到目标线程的结束,
pthread_join
函数将返回0;否则返回一个非零值。
注意事项
- 使用
pthread_join
函数可以避免主线程在子线程执行完毕前提前退出,确保线程间的正确同步和协作。 - 调用
pthread_join
函数后,目标线程的资源将被释放,其退出状态也将被获取。
总之,pthread_join
函数用于等待指定线程的结束,并获取该线程的退出状态,以确保在主线程继续执行前所有子线程都已经完成。
👉🏻线程的分离
在线程编程中,线程的分离是指将一个线程设置为分离状态,使得该线程在结束执行时能够自动释放其所占用的资源,而无需其他线程调用pthread_join
函数等待其结束。
🍉线程分离的概念
- 分离状态 :将线程设置为分离状态后,线程结束时会自动释放资源,无需其他线程调用
pthread_join
来获取线程的退出状态。 - 非分离状态 :默认情况下,线程是处于非分离状态的,需要通过
pthread_join
函数来等待线程结束并获取其退出状态。
🍉 分离线程的优势
- 资源管理 :分离线程可以简化资源管理,避免资源泄漏。
- 减少线程阻塞:主线程不需要等待所有子线程结束,可以提高程序的并发性和响应性。
- 线程生命周期独立 :分离线程的生命周期独立于其他线程,不会受其他线程的影响。
🍉 设置线程为分离状态
- 使用
pthread_attr_setdetachstate
函数设置线程属性为分离状态,然后在创建线程时使用该属性。
c
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&thread_id, &attr, start_routine, arg);
🍉注意事项
- 分离线程一旦设定,就无法再将其改回非分离状态。
- 分离线程不返回退出状态 ,因此不可调用
pthread_join
等待其结束。 - 需要确保线程不再需要与其他线程同步或通信时再设置为分离状态。
通过将线程设置为分离状态,可以简化线程的管理和资源释放,适用于那些不需要与其他线程同步的独立任务。
线程模式默认是
Joinable
可连接的
pthread_detach函数
在POSIX线程编程中,pthread_detach
函数用于将指定线程设置为分离状态,使得该线程在结束执行时能够自动释放其所占用的资源,无需其他线程调用pthread_join
函数等待其结束。
函数原型
c
int pthread_detach(pthread_t thread);
参数
thread
:要设置为分离状态的线程标识符。
功能
- 调用
pthread_detach
函数会将指定的线程设置为分离状态,使得该线程在结束执行时可以自动释放资源。 - 分离状态的线程结束时不会保留退出状态,也不需要其他线程调用
pthread_join
等待其结束。
返回值
- 如果成功 将线程设置为分离状态,则
pthread_detach
函数返回0;否则返回一个非零值。
注意事项
- 分离线程一旦设定,就无法再将其改回非分离状态。
- 分离线程不返回退出状态,因此不可调用
pthread_join
等待其结束。 - 需要确保线程不再需要与其他线程同步或通信时再设置为分离状态。
示例代码
c
pthread_t tid;
pthread_create(&tid, NULL, start_routine, arg); // 创建线程
pthread_detach(tid); // 将线程设置为分离状态
通过使用pthread_detach
函数,可以方便地将线程设置为分离状态,从而简化线程的管理和资源释放,适用于那些不需要与其他线程同步的独立任务。
POSIX线程编程
POSIX线程编程是指使用POSIX标准定义的线程库(pthread)来进行多线程编程的一种方式。POSIX(Portable Operating System Interface)是一个操作系统接口标准,为不同的操作系统提供了一致的API接口,使得开发者可以编写跨平台的应用程序。
💧 POSIX线程编程的特点:
-
跨平台性:由于POSIX标准的制定,使得使用POSIX线程库编写的多线程程序能够在不同的操作系统上运行,具有较好的可移植性。
-
多线程支持:POSIX线程库提供了丰富的函数和数据类型,方便开发者创建、管理和同步多个线程,实现并发执行和资源共享。
-
线程同步:POSIX线程库提供了各种同步机制,如互斥锁、条件变量等,帮助开发者解决多线程间的竞争和协作问题。
-
线程属性:可以通过设置线程属性来控制线程的行为,例如设置线程栈大小、分离状态等。
-
线程安全性:POSIX线程库中的函数通常是线程安全的,可以在多个线程中同时调用而不会出现问题。
💧 POSIX线程库常用函数:
pthread_create
:创建新线程。pthread_join
:等待线程结束并获取其退出状态。pthread_detach
:将线程设置为分离状态。pthread_mutex_init
:初始化互斥锁。pthread_cond_init
:初始化条件变量。- 等等。
💧使用示例:
c
#include <pthread.h>
void* thread_function(void* arg) {
// 线程执行的代码
return NULL;
}
int main() {
pthread_t tid;
pthread_create(&tid, NULL, thread_function, NULL);
// 其他主线程的工作
pthread_join(tid, NULL); // 等待线程结束
return 0;
}
通过POSIX线程编程,可以更方便地实现多线程并发操作,提高程序的性能和效率。
👉🏻如何理解pthread库来管理线程
理解pthread库来管理线程可以从以下几个方面展开:
-
创建线程 :使用pthread库可以方便地创建新的线程,通过调用
pthread_create
函数并传入线程执行的函数指针和参数,即可创建新的线程。 -
同步与互斥:pthread库提供了互斥锁(mutex)、条件变量(condition variable)等同步机制,可以帮助管理线程间的竞争和协作,避免资源访问冲突。
-
线程属性:可以通过pthread库设置线程的属性,如设置线程栈大小、分离状态等,以满足特定需求。
-
线程管理 :pthread库提供了对线程的管理功能,如等待线程结束并获取其退出状态(
pthread_join
)、将线程设置为分离状态(pthread_detach
)等。 -
线程安全性:pthread库中的函数通常是线程安全的,可以在多个线程中同时调用而不会出现问题。
-
多平台支持:由于POSIX标准的制定,使用pthread库编写的多线程程序能够在不同的操作系统上运行,具有较好的可移植性。
-
资源管理:通过pthread库可以管理线程所占用的资源,包括线程栈、线程局部存储等,使得资源的分配和释放更加灵活。
总的来说,理解pthread库来管理线程就是要掌握如何创建、同步、管理和释放线程,以及如何利用pthread库提供的丰富功能来编写高效、健壮的多线程程序。通过深入理解pthread库的各种函数和机制,开发者可以更加灵活地应对多线程编程中的各种挑战和需求。
c++的thread
C++11引入了对多线程编程的支持,其中包括了<thread>
头文件,该头文件中定义了std::thread
类,可以用于创建和管理线程。下面是关于C++的std::thread
类的一些重要信息:
创建线程:
cpp
#include <iostream>
#include <thread>
void thread_function() {
// 线程的具体操作
std::cout << "Hello from thread!" << std::endl;
}
int main() {
// 创建新线程
std::thread t(thread_function);
// 主线程的其他工作
// 等待新线程结束
t.join();
return 0;
}
线程管理:
std::thread
类提供了join()
和detach()
方法来管理线程的结束状态,分别表示等待线程结束和将线程设置为分离状态。
传递参数:
cpp
void thread_function(int n) {
std::cout << "Thread function with parameter: " << n << std::endl;
}
int main() {
int value = 42;
std::thread t(thread_function, value); // 传递参数给线程函数
t.join();
return 0;
}
后台线程:
cpp
int main() {
std::thread t(thread_function);
t.detach(); // 将线程设置为后台运行,不再等待其结束
// 主线程的其他工作
return 0;
}
线程安全性:
需要注意在多线程程序中访问共享数据时可能会出现竞争条件,需要使用互斥锁等机制来保证线程安全。
总的来说,C++的std::thread
类提供了方便的接口来开发多线程程序,通过它可以轻松地创建、管理和同步线程,同时也需要注意线程安全性和资源管理等问题,以确保多线程程序的正确性和健壮性。
👉🏻线程管理在用户空间中的位置
线程要具有独立属性:
1.上下文
2.栈
每个新线程的栈在库中维护
👉🏻线程的局部存储
线程的局部存储(Thread Local Storage,TLS)指的是每个线程拥有的独立内存空间,线程可以在这个空间中存储和访问自己私有的数据,而不会被其他线程访问或修改。线程的局部存储通常用于存储线程特有的数据,这些数据对于每个线程来说是唯一的,不同线程之间互不干扰。
🍎实现方式:
-
全局变量实现:可以通过全局变量加上线程ID的方式来实现线程的局部存储。每个线程使用不同的线程ID访问全局变量,以达到线程间数据隔离的效果。
-
pthread库提供的接口 :POSIX线程库提供了一系列函数来支持线程的局部存储,如
pthread_key_create
、pthread_setspecific
、pthread_getspecific
等函数。开发者可以使用这些函数来创建线程特有的键,并将数据与这些键关联起来,实现线程的局部存储。
🍎使用方式示例:
c
#include <pthread.h>
#include <stdio.h>
pthread_key_t key;
void destructor(void* value) {
printf("Cleaning up thread-specific data\n");
free(value);
}
void* thread_function(void* arg) {
char* thread_data = (char*)malloc(100);
pthread_setspecific(key, thread_data);
// 使用线程特有数据thread_data
return NULL;
}
int main() {
pthread_t tid;
pthread_key_create(&key, destructor);
pthread_create(&tid, NULL, thread_function, NULL);
// 其他主线程的工作
pthread_join(tid, NULL); // 等待线程结束
pthread_key_delete(key);
return 0;
}
在以上示例中,使用pthread_key_create
函数创建一个线程特有的键key
,并为每个线程分配独立的内存空间存储数据。在线程函数中使用pthread_setspecific
将线程特有的数据与键关联起来,实现线程的局部存储。
通过线程的局部存储机制,可以方便地实现线程间的数据隔离,确保每个线程操作的数据独立性,提高程序的健壮性和可维护性。
👉🏻clone函数
clone
函数是Linux系统中的一个系统调用,用于创建一个新的进程(或线程),并可以指定与父进程(或线程)共享某些资源,也可以在创建时对进程的行为进行一些控制。在某种程度上,clone
函数类似于fork
,但提供了更多的自定义选项。
clone
函数的原型:
c
int clone(int (*fn)(void *arg), void *child_stack, int flags, void *arg, ... /* pid_t *ptid, unsigned long newtls, pid_t *ctid */);
参数说明:
fn
:是一个函数指针,指向新进程(或线程)的入口函数。child_stack
:新进程(或线程)的栈空间地址,通常是以malloc
分配的一块内存。flags
:用于指定新进程(或线程)的一些特性,如共享哪些资源等。arg
:传递给新进程(或线程)的参数。ptid
:指向一个pid_t类型的指针,用于存储新进程(或线程)的线程ID。newtls
:用于设置新进程(或线程)的TLS(Thread Local Storage)指针。ctid
:指向一个pid_t类型的指针,用于存储新进程(或线程)的线程组ID。
使用示例:
c
#define _GNU_SOURCE
#include <stdio.h>
#include <sched.h>
#include <stdlib.h>
#include <unistd.h>
int thread_function(void *arg) {
printf("Child thread: %s\n", (char *)arg);
return 0;
}
int main() {
char *stack = malloc(16384); // 分配新线程的栈空间
int flags = CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_THREAD;
int pid = clone(thread_function, stack + 16384, flags, "Hello from child thread");
if (pid == -1) {
perror("clone");
exit(EXIT_FAILURE);
}
printf("Parent process: Child thread PID = %d\n", pid);
// 等待子线程执行完毕
sleep(2);
free(stack);
return 0;
}
在上面的示例中,通过clone
函数创建了一个新的线程,指定了共享虚拟内存、文件系统、文件描述符、信号处理等资源,并传递了一个参数给新线程。最后,父线程等待一段时间后释放内存并结束。
如上便是本期的所有内容了,如果喜欢并觉得有帮助的话,希望可以博个点赞+收藏+关注🌹🌹🌹❤️ 🧡 💛,学海无涯苦作舟,愿与君一起共勉成长