线程
(注意:线程相关函数在编译时记得链接上线程库)
1.线程的概念
线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。
一个进程可以包含多个线程,这些线程共享进程所拥有的资源,如内存空间、文件描述符等。线程有自己的堆栈、程序计数器等少量的私有数据。
线程的主要优点包括提高程序的并发性,使得多个任务能够在同一进程内并发执行,从而提高系统的资源利用率和响应性能。线程之间的切换相较于进程切换,通常开销更小,因此能够更高效地实现多任务处理。
在多线程编程中,需要注意线程同步和互斥等问题,以确保线程之间能够正确、安全地共享资源和协调工作。
2.线程和进程的区别
进程和线程主要有以下区别:
1. 定义和资源拥有:进程是资源分配的基本单位,拥有独立的地址空间和资源,如内存、文件描述符等。线程是进程中的执行单元,多个线程共享所属进程的资源。
2. 调度:进程的切换开销通常较大,涉及到更多的系统资源和状态切换。线程的切换开销相对较小,因为线程共享进程的资源环境。
3. 并发性:进程之间的并发性相对较低,因为它们的资源独立,通信和同步相对复杂。线程之间的并发性更高,可以更高效地实现并发执行。
4. 系统开销:创建和销毁进程的系统开销较大。创建和销毁线程的开销较小。
5. 通信:进程间通信通常需要使用特定的机制,如管道、消息队列、共享内存等,较为复杂。线程间通信相对简单,可以直接访问共享的进程资源进行通信。
6. 健壮性:一个进程的错误通常不会影响其他进程。一个线程的错误可能会影响整个进程的运行。
总之,进程和线程在资源管理、调度开销、并发性等方面存在差异,根据具体的应用场景和需求来选择使用进程或线程来实现程序的功能。
3.线程的设计框架
常见的线程设计框架有以下几种:
1. 生产者 - 消费者模式:存在生产者线程和消费者线程。生产者负责生成数据并放入共享缓冲区,消费者从缓冲区中取出数据进行处理。通过这种方式实现线程之间的协同工作。
2. 主从模式:有一个主线程负责分配任务,多个从线程负责执行具体任务。主线程控制任务的分发和整体流程,从线程专注于完成具体的计算或操作。
3. 线程池模式:创建一个线程池,其中包含一定数量的线程。任务被提交到线程池,线程池中的线程从任务队列中获取任务并执行。这种模式可以避免频繁创建和销毁线程的开销,提高线程的复用率。
在实际的线程设计中,还需要考虑线程同步、互斥、资源竞争等问题,以确保线程的正确执行和数据的一致性。同时,还需要根据具体的应用场景和性能需求,选择合适的线程设计框架。
4.线程相关函数
1.pthread_create
pthread_create 是 POSIX 线程库( pthreads )中的一个函数,用于创建新的线程。
其函数原型通常为:
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
参数说明:
- thread :用于返回新创建线程的标识符。
- attr :指定线程的属性,通常设置为 NULL 以使用默认属性。
- start_routine :是一个函数指针,指向新线程将执行的函数。
- arg :传递给新线程执行函数的参数。
函数返回值:如果成功创建线程,返回 0;否则返回错误码。
使用 pthread_create 函数创建线程时,需要注意线程的同步、资源共享和竞争等问题,以确保程序的正确性和稳定性。
注意:一次pthread_create执行只能创建一个线程。
每个进程至少有一个线程称为主线程。
主线程退出则所有创建的子线程都退出。
主线程必须有子线程同时运行才算多线程程序。
线程id是线程的唯一标识,是CPU维护的一组数字。
pstree 查看系统中多线程的对应关系。
多个子线程可以执行同一回调函数。
ps -eLf 查看线程相关信息Low Weigth Process
ps -eLo pid,ppid,lwp,stat,comm
2.pthread_self
函数原型:
pthread_t pthread_self(void); unsigned long int; %lu
pthread_self(void) 是 POSIX 线程库中的一个函数,用于获取调用该函数的线程自身的线程标识符。
其返回值类型为 pthread_t ,表示当前线程的唯一标识符。
例如:
#include <pthread.h>
#include <stdio.h>
void *threadFunction(void *arg) {
pthread_t tid = pthread_self();
printf("Thread ID in threadFunction: %lu\n", (unsigned long)tid);
return NULL;
}
int main() {
pthread_t thread;
pthread_create(&thread, NULL, threadFunction, NULL);
pthread_t mainTid = pthread_self();
printf("Thread ID in main: %lu\n", (unsigned long)mainTid);
pthread_join(thread, NULL);
return 0;
}
在上述示例中,在主线程和新创建的线程中分别调用 pthread_self 来获取各自的线程标识符。
3. pthread_exit
pthread_exit 是 POSIX 线程库中的一个函数,用于终止当前线程。
函数原型为:
void pthread_exit(void *retval);
参数 retval 是一个指针,用于指定线程的返回值。其他线程可以通过 pthread_join 函数获取到这个返回值。
当一个线程调用 pthread_exit 后,该线程就会结束执行,释放其占用的资源。但需要注意的是,如果线程是可结合的(默认情况),并且没有其他线程调用 pthread_join 来等待它结束,可能会导致资源泄漏。
4.pthread_cancel
pthread_cancel 是 POSIX 线程库中的一个函数,用于请求取消另一个线程的执行。
函数原型为:
int pthread_cancel(pthread_t thread);
参数 thread 是要取消的目标线程的标识符。
需要注意的是,使用 pthread_cancel 并不一定能立即终止目标线程。目标线程可以通过设置特定的取消处理函数和状态来决定如何响应取消请求。如果目标线程正在执行一些不可取消的操作(例如系统调用),那么取消操作可能会延迟到这些操作完成之后。
另外,即使发送了取消请求,目标线程也需要在适当的检查点检查是否有取消请求并进行相应的处理。
5. pthread_join
pthread_join 是 POSIX 线程库中的一个函数,用于等待另一个线程结束,并获取其返回值。
函数原型为:
int pthread_join(pthread_t thread, void **retval);
参数说明:
- thread :要等待结束的线程的标识符。
- retval :用于接收被等待线程的返回值。
函数返回值:成功返回 0 ,失败返回错误码。
使用 pthread_join 可以确保在被等待线程结束之前,当前线程不会继续执行,从而实现线程之间的同步和资源回收。
6. pthread_attr_init
pthread_attr_init 函数用于初始化线程属性对象。
函数原型:
int pthread_attr_init(pthread_attr_t *attr);
参数:
- attr :指向要初始化的线程属性对象的指针。
返回值:
- 成功时返回 0 。
- 出错时返回错误码。
在使用线程属性之前,通常需要先调用 pthread_attr_init 进行初始化,然后可以通过其他相关函数(如 pthread_attr_setdetachstate 等)来设置属性的具体参数,最后将设置好的属性对象传递给 pthread_create 函数来创建具有特定属性的线程。
例如:
#include <pthread.h>
#include <stdio.h>
void* thread_function(void* arg) {
printf("Hello from thread!\n");
pthread_exit(NULL);
}
int main() {
pthread_attr_t attr;
pthread_t thread_id;
int ret;
ret = pthread_attr_init(&attr);
if (ret!= 0) {
printf("Error initializing thread attributes\n");
return 1;
}
// 设置其他属性(可选)
ret = pthread_create(&thread_id, &attr, thread_function, NULL);
if (ret!= 0) {
printf("Error creating thread\n");
return 1;
}
// 销毁属性对象
pthread_attr_destroy(&attr);
return 0;
}
在上述示例中,先初始化了线程属性对象,然后创建线程,最后销毁属性对象以释放相关资源。
7.pthread_attr_destroy
pthread_attr_destroy 函数用于销毁一个已初始化的线程属性对象,释放其相关资源。
函数原型:
int pthread_attr_destroy(pthread_attr_t *attr);
参数:
- attr :指向要销毁的线程属性对象的指针。
返回值:
- 成功时返回 0 。
- 出错时返回错误码。
在使用完线程属性对象后,调用 pthread_attr_destroy 来进行清理,以避免内存泄漏和资源浪费。
8.pthread_attr_setdetachstate
pthread_attr_setdetachstate 函数用于设置线程属性对象中的分离状态属性。
函数原型:
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
参数:
- attr :指向线程属性对象的指针。
- detachstate :指定的分离状态,通常可以是 PTHREAD_CREATE_DETACHED (创建分离线程)或 PTHREAD_CREATE_JOINABLE (创建可连接线程)。
返回值:
- 成功时返回 0 。
- 出错时返回错误码。
通过这个函数,可以在创建线程之前决定线程是分离的还是可连接的。
9.pthread_deatch
pthread_detach 函数用于将指定的线程设置为分离状态。
函数原型:
int pthread_detach(pthread_t thread);
参数:
- thread :要设置为分离状态的线程标识符。
返回值:
- 成功时返回 0 。
- 出错时返回错误码。
当一个线程被分离后,它在终止时会自动释放其占用的系统资源,而不需要其他线程通过调用 pthread_join 来获取其终止状态和回收资源。
使用 pthread_detach 函数可以避免资源泄漏和不必要的等待。需要注意的是,一旦线程被分离,就不能再对其进行 pthread_join 操作。
例如:
#include <pthread.h>
#include <stdio.h>
void* thread_function(void* arg) {
printf("Hello from thread!\n");
pthread_exit(NULL);
}
int main() {
pthread_t thread_id;
int ret;
ret = pthread_create(&thread_id, NULL, thread_function, NULL);
if (ret!= 0) {
printf("Error creating thread\n");
return 1;
}
ret = pthread_detach(thread_id);
if (ret!= 0) {
printf("Error detaching thread\n");
return 1;
}
// 主线程可以继续执行其他任务,无需等待子线程结束
return 0;
}
在上述示例中,创建了一个子线程,并使用 pthread_detach 将其设置为分离状态,主线程无需等待子线程结束即可继续执行。
10.pthread_cleanup_push
pthread_cleanup_push 函数用于将清理函数压入线程的清理栈。
函数原型:
void pthread_cleanup_push(void (*routine)(void *), void *arg);
参数:
- routine :指向清理函数的指针,当线程以特定方式退出(如被取消或正常退出)时,会调用这个清理函数。
- arg :传递给清理函数的参数。
当使用 pthread_cleanup_push 时,通常需要与 pthread_cleanup_pop 成对出现,以确保清理操作的正确执行。
例如:
#include <pthread.h>
#include <stdio.h>
void cleanup_func(void *arg) {
printf("Cleanup function called\n");
}
void* thread_function(void *arg) {
pthread_cleanup_push(cleanup_func, NULL);
// 线程的执行代码
pthread_cleanup_pop(0);
pthread_exit(NULL);
}
int main() {
pthread_t thread_id;
int ret;
ret = pthread_create(&thread_id, NULL, thread_function, NULL);
if (ret!= 0) {
printf("Error creating thread\n");
return 1;
}
ret = pthread_join(thread_id, NULL);
if (ret!= 0) {
printf("Error joining thread\n");
return 1;
}
return 0;
}
在上述示例中,当线程正常退出或被取消时,都会调用 cleanup_func 清理函数。
11.pthread_cleanup_pop
pthread_cleanup_pop 函数用于从线程的清理栈中弹出一个之前通过 pthread_cleanup_push 压入的清理函数.
函数原型:
void pthread_cleanup_pop(int execute);
参数:
- execute :一个整数参数,如果为非零值,则立即执行弹出的清理函数;如果为 0 ,则不执行。
pthread_cleanup_push 和 pthread_cleanup_pop 通常成对使用,用于在线程退出时执行一些必要的清理操作。