Linux中的线程(Threads)是轻量级进程(Lightweight Processes,LWP),它们共享相同的地址空间和大部分资源,但每个线程都有自己独立的栈、寄存器和程序计数器。线程允许程序在单个进程内并发执行多个任务,提高程序的执行效率和响应速度。下面是对Linux线程的一些基本介绍
线程与进程
线程是应用程序并发执行多种任务的一种机制。在一个进程中可以创建多个线程执行多个任务。每个进程都可以创建多个线程,创建的多个线程共享进程的地址空间,即线程被创建在进程所使用的地址空间上。创建子进程是通过复制父进程的地址空间得来的,父子进程只关注自己的地址空间(映射不同的物理地址空间)。因此进程与进程之间是独立的,每个进程都只需要操作属于自己的地址空间即可。而线程则不一样,创建线程无须对地址空间进行复制,同一个进程创建的线程共享进程的地址空间,因此创建线程比创建子进程要快很多。
如图所示,一个进程可以包含多个线程。因此,同一个程序中的所有线程均会执行相同的程序,且共享进程的内存段,包括数据段、堆区。值得注意的是,进程的栈区对线程是不共享的,每个线程都拥有属于自己的栈区,用于存放函数的参数值、局部变量的值、返回地址等。
使用多线程的理由
使用多线程的理由之一是和进程相比,它是一种非常"节俭"的多任务操作方式。我们知道,在Linux系统下,启动一个新的进程必须分配给它独立的地址空间,建立众多的数据表来维护它的代码段、堆栈段和数据段,这是一种"昂贵"的多任务工作方式。而运行于一个进程中的多个线程,它们彼此之间使用相同的地址空间,共享大部分数据,启动一个线程所花费的空间远远小于启动一个进程所花费的空间,而且,线程间彼此切换所需的时间也远远小于进程间切换所需要的时间。据统计,总的说来,一个进程的开销大约是一个线程开销的30倍左右,当然,在具体的系统上,这个数据可能会有较大的区别。
使用多线程的理由之二是线程间方便的通信机制。对不同进程来说,它们具有独立的数据空间,要进行数据的传递只能通过通信的方式进行,这种方式不仅费时,而且很不方便。线程则不然,由于同一进程下的线程之间共享数据空间,所以一个线程的数据可以直接为其它线程所用,这不仅快捷,而且方便。当然,数据的共享也带来其他一些问题,有的变量不能同时被两个线程所修改,有的子程序中声明为static的数据更有可能给多线程程序带来灾难性的打击,这些正是编写多线程程序时最需要注意的地方。
除了以上所说的优点外,不和进程比较,多线程程序作为一种多任务、并发的工作方式,当然有以下的优点:
- 提高应用程序响应。这对图形界面的程序尤其有意义,当一个操作耗时很长时,整个系统都会等待这个操作,此时程序不会响应键盘、鼠标、菜单的操作,而使用多线程技术,将耗时长的操作(time
consuming)置于一个新的线程,可以避免这种尴尬的情况。 - 使多CPU系统更加有效。操作系统会保证当线程数不大于CPU数目时,不同的线程运行于不同的CPU上。
- 改善程序结构。一个既长又复杂的进程可以考虑分为多个线程,成为几个独立或半独立的运行部分,这样的程序会利于理解和修改
多线程开发API概要
多线程开发在 Linux 平台上已经有成熟的 pthread 库支持。其涉及的多线程开发的最基本概念主要包含三点:线程,互斥锁,条件。其中,线程操作又分线程的创建,退出,等待 3 种。互斥锁则包括 4 种操作,分别是创建,销毁,加锁和解锁。条件操作有 5 种操作:创建,销毁,触发,广播和等待。其他的一些线程扩展概念,如信号灯等,都可以通过上面的三个基本元素的基本操作封装出来。详细请见下表:
pthread_detach分离线程函数
在Linux多线程编程中,pthread_detach是一个函数,用于将一个线程设置为分离状态(detached state)。这意味着该线程在结束时会自动释放其资源,而不需要其他线程显式地清理这些资源。这使得分离的线程非常适合那些不需要等待其完成的任务。
函数原型
#include <pthread.h>
int pthread_detach(pthread_t thread);
thread: 是要分离的线程的标识符。
返回值
pthread_detach函数返回以下值之一:
- 0: 成功分离线程。
- 非零值: 发生错误,并且errno将被设置为相应的错误码。
错误码
如果pthread_detach失败,它可能会设置以下errno值之一:
- EINVAL: 指定的线程ID无效。
- ESRCH: 没有找到与给定线程ID匹配的线程。
- EINVAL: 线程已经分离或已经终止。
使用场景
- 后台任务:
当你创建一个线程来执行后台任务,如日志记录或数据同步,这些任务通常不需要等待完成。在这种情况下,你可以分离线程,以便主线程或其他线程可以继续执行而不必等待这些后台任务完成。 - 一次性任务:
对于只需要执行一次的任务,分离线程可以简化代码结构,避免需要显式地等待线程完成。
这样不需要在父线程中join等待它结束了
线程
线程的创建
#include <stdio.h>
#include <pthread.h>
// 线程函数原型:
// int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(*start_rtn)(void *), void *restrict arg);
// 线程函数:线程创建后将运行此函数
void *func1(void *arg)
{
// 定义一个静态字符指针,指向字符串 "t1 is run out"
static char *p = "t1 is run out";
// 打印线程ID(通过 pthread_self 获取)
printf("t1:%ld thread is create\n", (unsigned long)pthread_self());
// 打印传入的参数值(通过类型转换和解引用获取)
printf("t1:param is %d\n", *((int *)arg));
// 退出线程并返回指针 p
pthread_exit((void *)p);
}
int main()
{
int ret; // 存储 pthread_create 的返回值
int param = 100; // 将作为参数传递给线程函数
pthread_t t1; // 线程标识符,用于存储创建的线程ID
char *pret = NULL; // 用于存储线程函数的返回值
// 创建线程 t1,执行函数 func1,传递参数 ¶m
ret = pthread_create(&t1, NULL, func1, (void *)¶m);
if (ret == 0) {
// 如果线程创建成功,打印成功消息
printf("main:create t1 success\n");
}
// 打印主线程的线程ID
printf("main:%ld\n", (unsigned long)pthread_self());
// 等待线程 t1 结束,并获取其返回值
pthread_join(t1, (void **)&pret);
// 打印线程 t1 的返回值
printf("main: t1 quit: %s\n", pret);
return 0; // 返回0表示程序正常结束
}
互斥锁
#include <stdio.h>
#include <pthread.h>
#include <unistd.h> // 包含sleep函数
// 声明pthread_create函数的原型
// int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(*start_rtn)(void *), void *restrict arg);
int g_data = 0; // 全局变量,用于线程间共享和修改的数据
pthread_mutex_t mutex; // 定义互斥锁,用于保护全局变量g_data的访问
// 线程函数1
void *func1(void *arg)
{
// 打印线程ID和传入的参数值
printf("t1:%ld thread is create\n", (unsigned long)pthread_self());
printf("t1:param is %d\n", *((int *)arg));
pthread_mutex_lock(&mutex); // 加锁,保护g_data的访问
while(1) {
printf("t1: %d\n", g_data++); // 打印并增加g_data
sleep(1); // 休眠1秒
// 如果g_data等于3,解锁并退出线程
if (g_data == 3) {
pthread_mutex_unlock(&mutex); // 解锁
printf("t1 quit================================\n");
// pthread_exit(NULL); // 可以使用pthread_exit退出线程
exit(0); // 退出程序(注意:此行会导致整个进程退出,包括主线程和其他线程)
}
}
}
// 线程函数2
void *func2(void *arg)
{
// 打印线程ID和传入的参数值
printf("t2:%ld thread is create\n", (unsigned long)pthread_self());
printf("t2:param is %d\n", *((int *)arg));
while(1) {
printf("t2: %d\n", g_data); // 打印g_data
pthread_mutex_lock(&mutex); // 加锁,保护g_data的访问
g_data++; // 增加g_data
pthread_mutex_unlock(&mutex); // 解锁
sleep(1); // 休眠1秒
}
}
int main()
{
int ret;
int param = 100; // 用于传递给线程函数的参数
pthread_t t1, t2; // 线程标识符
pthread_mutex_init(&mutex, NULL); // 初始化互斥锁
// 创建线程t1,执行函数func1,传递参数¶m
ret = pthread_create(&t1, NULL, func1, (void *)¶m);
if (ret == 0) {
printf("main:create t1 success\n");
}
// 创建线程t2,执行函数func2,传递参数¶m
ret = pthread_create(&t2, NULL, func2, (void *)¶m);
if (ret == 0) {
printf("main:create t2 success\n");
}
// 打印主线程的线程ID
printf("main:%ld\n", (unsigned long)pthread_self());
while (1) {
printf("main: %d\n", g_data); // 打印g_data
sleep(1); // 休眠1秒
}
pthread_join(t1, NULL); // 等待线程t1结束
pthread_join(t2, NULL); // 等待线程t2结束
pthread_mutex_destroy(&mutex); // 销毁互斥锁
return 0; // 返回0表示程序正常结束
}
条件
#include <stdio.h>
#include <pthread.h>
#include <unistd.h> // 包含sleep函数
// 声明pthread_create函数的原型
// int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(*start_rtn)(void *), void *restrict arg);
int g_data = 0; // 全局变量,用于线程间共享和修改的数据
// 初始化互斥锁和条件变量
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
// 线程函数1
void *func1(void *arg)
{
// 打印线程ID和传入的参数值
printf("t1:%ld thread is create\n", (unsigned long)pthread_self());
printf("t1:param is %d\n", *((int *)arg));
static int cnt = 0; // 静态变量,用于记录循环次数
while(1) {
// 等待条件变量,等待时释放互斥锁,条件满足后重新加锁
pthread_cond_wait(&cond, &mutex);
printf("t1 run================================\n");
// 打印并重置g_data
printf("t1: %d\n", g_data);
g_data = 0;
sleep(1); // 休眠1秒
// 循环计数器,达到10次时退出程序
if (cnt++ == 10) {
exit(1);
}
}
}
// 线程函数2
void *func2(void *arg)
{
// 打印线程ID和传入的参数值
printf("t2:%ld thread is create\n", (unsigned long)pthread_self());
printf("t2:param is %d\n", *((int *)arg));
while(1) {
// 打印g_data
printf("t2: %d\n", g_data);
pthread_mutex_lock(&mutex); // 加锁,保护g_data的访问
g_data++; // 增加g_data
// 如果g_data等于3,发送条件信号
if (g_data == 3) {
pthread_cond_signal(&cond);
}
pthread_mutex_unlock(&mutex); // 解锁
sleep(1); // 休眠1秒
}
}
int main()
{
int ret;
int param = 100; // 用于传递给线程函数的参数
pthread_t t1, t2; // 线程标识符
// 创建线程t1,执行函数func1,传递参数¶m
ret = pthread_create(&t1, NULL, func1, (void *)¶m);
if (ret == 0) {
// printf("main:create t1 success\n");
}
// 创建线程t2,执行函数func2,传递参数¶m
ret = pthread_create(&t2, NULL, func2, (void *)¶m);
if (ret == 0) {
// printf("main:create t2 success\n");
}
// printf("main:%ld\n", (unsigned long)pthread_self());
pthread_join(t1, NULL); // 等待线程t1结束
pthread_join(t2, NULL); // 等待线程t2结束
pthread_mutex_destroy(&mutex); // 销毁互斥锁
pthread_cond_destroy(&cond); // 销毁条件变量
return 0; // 返回0表示程序正常结束
}