在Linux系统中,每个进程都有自己的地址空间,它由多个段(区)组成,包括代码区、字符常量区、已初始化数据区、未初始化数据区、堆、栈以及内核区等,所谓地址空间是指进程可用于寻址内存的一系列地址。 CUP内的寄存器指向进程的PCB(PCB是指操作系统内核中用于管理进程的数据结构
),当然还有寄存器指向栈顶,页表等;进程的PCB指向它自己的地址空间,这样一个进程间就被建立起来。
如果再创建一个子进程,那么需要创建自己的地址空间、页表等,有一种方法只创建PCB,不再创建地址空间等,而和父进程共用一套地址空间、页表。在代码区可以设置不同条件,相当于不同的条件下调用不同的函数;同一个进程的每一个不同的PCB相当于一个执行流,他们执行相同代码区里的不同代码,像这样进程里创建多个PCB,将这种PCB称为线程。
如果说操作系统中引入进程的目的是使多个程序能并发执行,以提高资源利用率和系统吞吐量,那么再引入线程则是为了减少程旭在并发执行时所付出的时间和空间开销,以使操作系统具有更好的并发性。
线程是计算机科学中的一个概念,它是进程中的一个实体,是CPU调度和分派的基本单位。 线程与进程的区别在于,线程是进程中的一个实体,而进程则是操作系统资源分配的基本单位。 在Linux系统中,线程被称为轻量级进程(LWP),因为它们与其父进程共享许多资源,如地址空间、打开文件等。
CPU与操作系统的概念
:CPU(中央处理器)是计算机的核心部件,负责执行指令,控制计算机的运行。操作系统则是计算机的软件部分,它管理计算机的硬件资源,为应用程序提供服务。CPU和操作系统之间的区别在于,CPU是计算机硬件的一部分,而操作系统是计算机软件的一部分。 CPU负责执行指令,而操作系统负责管理和控制计算机的各种资源,如内存、硬盘、输入输出设备等。此外,操作系统还提供了一些基本服务,如文件管理、进程管理、内存管理等。所以线程的粒度比进程的粒度更细。
Linux下的多线程编程可以使用pthread库实现。Linux系统中并没有真正意义上的多线程,因为linux内核中并没有为线程构建数据结构,而是用进程方案模拟线程。(windows是有真线程的) 使用pthread库,可以使用以下函数实现多线程:
pthread_create
:创建线程pthread_join
:等待线程结束pthread_exit
:退出线程pthread_self
:获取线程IDpthread_detach
:分离线程。以上函数都是在头文件pthread.h
中定义的。其中,pthread_create()
函数用于创建一个新的线程,它的原型如下:
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg);
thread
参数是一个指向线程标识符的指针,当线程创建成功后,用来返回创建的线程ID;attr
参数用于指定线程的属性,NULL表示使用默认属性;start_routine
参数为一个函数指针,指向线程创建后要调用的函数,这个函数也称为线程函数;arg
参数指向传递给线程函数的参数。返回值:线程创建成功则返回0,发生错误时返回错误码。
在Linux系统下,多线程遵循POSIX线程接口,称为pthread。编写Linux下的多线程程序,需要使用头文件pthread.h,连接时需要使用库libpthread.a。Linux下pthread是通过系统调用clone()来实现的。clone()是Linux所特有的系统调用,它的使用方式类似于fork()。
线程的优点:
线程的缺点:
总的来说,多线程的优点是可以提高程序的并发性和效率,缺点是增加了系统调用的代价和内核资源的消耗。
在Linux中,线程是复用进程数据结构,那么Linux怎么识别一个执行流是线程还是进程?怎么确定线程是并发执行的?
#include
#include //包含线程的头文件
#include
using namespace std;
void *thread1_run(void* args)
{
while (1)
{
cout << "我是线程1,我正在运行" << endl;
sleep(1);
}
}
void *thread2_run(void* args)
{
while (1)
{
cout << "我是线程2,我正在运行" << endl;
sleep(1);
}
}
void *thread3_run(void* args)
{
while (1)
{
cout << "我是线程3,我正在运行" << endl;
sleep(1);
}
}
int main()
{
pthread_t t1, t2, t3;
pthread_create(&t1, nullptr, thread1_run, nullptr);//线程属性还有传递线程函数的参数设为空
pthread_create(&t2, nullptr, thread2_run, nullptr);
pthread_create(&t3, nullptr, thread3_run, nullptr);
while (1)
{
cout << "我是主线程,我正在运行" << endl;
sleep(1);
}
return 0;
}
由下图可以看出,有四个死循环,四个循环同时打印,说明线程是并发式执行的;如果不是并发执行,那么会卡到某一个循环中。(用g++编译时,如果头文件包含pthread.h,则编译时需要链接pthread库,如:g++ -o mythread mythread.cc -lpthread
)
使用指令ps -aL | head -1 && ps -aL | grep mythread(文件名)
,查看线程信息,可以看到他们的PID是相同的,但是LWP不同,所以线程是用LWP来区分的。
多线程程序中,任何一个线程崩溃了,最后都会导致进程崩溃;系统角度:线程是进程的执行分支,线程崩溃,进程就崩溃了。
1.pthread_join()
函数是用来等待一个线程结束,并获取它的返回值的。它的参数有两个:
pthread_t thread
:要等待的线程的标识符,也就是线程ID。
void **retval
:一个指向指针的指针,用来存储线程的返回值。如果不需要获取返回值,可以传入NULL。
pthread_join()函数会阻塞调用它的线程,直到目标线程结束为止。如果目标线程已经结束,或者是分离状态的,那么pthread_join()函数会失败,并返回相应的错误码。
2.pthread_exit()
函数是用来终止调用它的线程的,它可以通过一个指针参数返回线程的退出状态,供其他线程获取。它还会执行线程清理函数和线程特定数据的析构函数。pthread_exit()函数不会释放进程共享的资源,也不会调用atexit()注册的函数。如果主线程调用了pthread_exit()函数,那么其他线程仍然可以继续执行,直到进程结束。pthread_exit()函数和return语句的区别是,return语句会导致整个进程终止,而pthread_exit()函数只会终止当前线程。
3.pthread_cancel()
函数是用来向一个线程发送终止请求的,它的语法格式是:
int pthread_cancel(pthread_t thread);
其中,thread参数是要终止的线程的标识符。如果函数成功地发送了终止请求,返回0;否则返回错误码。注意,发送成功并不意味着线程会立即终止,而是取决于线程的取消状态和取消类型。
线程的取消状态有两种:启用(PTHREAD_CANCEL_ENABLE)和禁用(PTHREAD_CANCEL_DISABLE)。启用状态下,线程会响应终止请求;禁用状态下,线程会忽略终止请求,直到重新启用为止。
4.pthread_create函数
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg);
thread
参数是一个指向线程标识符的指针,当线程创建成功后,用来返回创建的线程ID;attr
参数用于指定线程的属性,NULL表示使用默认属性;start_routine
参数为一个函数指针,指向线程创建后要调用的函数,这个函数也称为线程函数;arg
参数指向传递给线程函数的参数。#include
#include
#include
using namespace std;
#define NUM 10
enum{ OK=0, ERROR };
class ThreadData
{
public:
ThreadData(const string &name, int id, time_t createTime, int top)
:_name(name), _id(id), _createTime((uint64_t)createTime),_status(OK), _top(top), _result(0)
{}
~ThreadData()
{}
public:
string _name;
int _id;
uint64_t _createTime;
// 返回的
int _status;
int _top;
int _result;
};
void *thread_run(void *args)
{
ThreadData *td = static_cast(args);
for(int i = 0; i <= td->_top; i++)
{
td->_result += i;
}
cout << td->_name << " cal done!" << endl;
return td;
}
int main()
{
pthread_t tids[NUM];
for(int i = 0; i < NUM ;i++)
{
char tname[64];
snprintf(tname, 64, "thread-%d", i+1);
ThreadData *td = new ThreadData(tname, i+1, time(nullptr), i + 1);
pthread_create(tids+i, nullptr, thread_run, td);
sleep(1);
}
void *ret = nullptr;
for(int i = 0 ; i< NUM; i++)
{
int n = pthread_join(tids[i], &ret);
if(n != 0) cerr << "pthread_join error" << endl;
ThreadData *td = static_cast*>(ret);
if(td->_status == OK)
{
cout << td->_name << " 计算的结果是: " << td->_result << " (它要计算的是[0, " << td->_top << "])" <;
}
delete td;
}
}