一、POSIX线程库
- 与线程有关的函数构成了一个完整的系列,绝大多数函数的名字都是以“pthread_"开头的
- 要使用这些函数库,要通过引入头文件<pthread.h>
- 连接这些线程函数库时要使用编译器命令的“-lpthread”选项
二、线程的一些函数
2.1 创建线程——pthread_create函数
2.1.1 函数介绍
函数的原型:
函数的功能:
创建一个新的线程
函数的参数:
- thread:输出型参数,返回线程的ID
- attr:设置线程的属性,一般我们使用默认属性,将attr设为NULL
- start_routine:是一个函数地址,线程启动后要执行的函数
- arg:传给线程启动函数的参数
函数的返回值:
- 成功返回0
- 失败返回错误码
2.1.2 错误检查
- 传统的一些函数是:成功返回0,失败返回-1,并且对全局变量errno赋值用来指示错误
- pthreads函数出错时不会设置全局变量errno(而大部分的其他POSIX函数会这样做),而是将错误码通过返回值返回
- pthreads同样也提供了线程内的errno变量,以支持其他使用errno的代码,对于pthreads函数的错误,建议通过返回值来判定,因为读取返回值要比读取线程内的errno变量的开销更小
2.1.3 举例代码
#include <iostream>
#include <pthread.h>
#include <sys/types.h>
#include <unistd.h>
// 新线程
void* threadStart(void* arg)
{
while (true)
{
std::cout << "thread-new running..." << "pid: " << getpid() << std::endl;
sleep(1);
}
}
int main()
{
pthread_t tid;
// 线程的ID 设置属性 线程要执行的函数 执行函数的参数
pthread_create(&tid, nullptr, threadStart, (void*)"thread-new");
// 主线程
while (true)
{
sleep(1);
std::cout << "main thread running..." << "pid: " << getpid() <<std::endl;
}
return 0;
}
2.1.4 一些细节
问题一:新线程和老线程谁先运行?
不确定,因为在计算机中,线程的调度是不确定的。
问题二:我们期望这个线程谁最后退出?
我们希望主线程最后退出,因为主线程还要等待其他线程,进行回收。
问题三:如何看待线程函数传参?
我们可以向该参数传递任何类型的数据,包括类对象的地址,但要记得该对象的成员...
2.1.5 如何创建多个线程?
#include <iostream>
#include <pthread.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string>
#include <cstring>
#include <unistd.h>
#include <vector>
void* threadrun(void* args)
{
std::string name = static_cast<const char*>(args);
while (true)
{
std::cout << name << "is running" << std::endl;
sleep(1);
}
}
int main()
{
std::vector<pthread_t> pthreads;
for(int i = 0; i < 10; i++)
{
pthread_t tid;
// 线程的名字
char* name = (char*)malloc(sizeof(int) * 128);
snprintf(name, sizeof name, "thread-%d", i + 1);
int n = pthread_create(&tid, nullptr, threadrun, name);
//sleep(1);
pthreads.push_back(tid); // 将创建出来的线程id存储在pthreads数组中
}
return 0;
}
2.2 线程等待函数——pthread_join函数
2.2.1 函数介绍
函数的原型:
函数的功能:
主线程等待新线程的pid,等待线程的结束,如果我们不进行等待线程,那么会照成线程的内存泄漏,但是要注意没有“
僵尸线程”的概念。函数的参数:
- thread:线程ID
- value_ptr:指向一个指针,后者指向线程的返回值
函数的返回值:
- 成功返回0
- 失败返回错误码
2.2.2 函数的第二个参数
调用该函数的线程将挂起等待,直到ID为thread的线程终止。thread线程以不同的方式终止,通过pthread_join得到的终止状态是不同的,总结如下:
- 如果thread线程通过return返回,value_ptr所指向的单元里存放的是thread线程函数的返回值
- 如果thread线程被别的线程调用pthread_cancel异常终止,value_ptr所指向的单元里存放的是常数PTHREAD_CANCELED
- 如果thread线程是自己调用pthread_exit终止的,value_ptr所指向的单元存放的是传给pthread_exit的参数
- 如果对thread线程的终止状态不感兴趣,可以传NULL给value_ptr参数
如何全面看待线程的返回?
线程的返回值只有正确的返回值,当线程出现异常后,直接全部崩溃,没有错误返回的机会,所以,线程在退出的时候,我们只需要考虑结果对不对,一个线程只要写崩溃,其他线程也会崩溃
我们也可以传递任意类型
2.2.3 线程访问的变量最好在堆空间中
在一个主函数中,如果线程需要访问的数据是在主函数的栈空间的,那么我们需要谨慎。如果说,只有一个线程还好,但是如果有两个及其以上的线程需要访问的话,会有一定的错误发生。因为一个线程将其值进行修改,会影响到另一个线程,所以我们需要进行malloc变量,然后使进程访问在堆开辟的空间交给对应的线程,每一个线程人手一个堆空间,不会进行相互影响。
2.3 线程ID以及进程地址空间布局——pthread_self函数
2.3.1 函数介绍
函数的原型:
函数的功能:
我们可以使用这个函数返回指定线程的线程ID
函数的返回值:
线程ID
2.3.2 介绍一下线程id
- pthread_create函数会产生一个线程ID,存放在第一个参数指向的地址中,LWP和线程ID不是已回事情
- LWP属于进程调度的范畴,因为线程是轻量级进程,是操作系统调度器的最小单位,所以需要一个数值来唯一表示该线程
- pthread_create函数的第一个参数指向一个虚拟地址空间,该内存单元即为新创建的线程的线程ID,属于NPTL线程库的范畴,线程库的后续操作,就是根据该线程ID来操作线程的
- 线程库NPTL提供了pthread_self函数,可以获得线程自身的ID
2.3.3 来简要介绍一下pthread_t类型
这种类型取决于实现,对于LInux目前实现的NPTL实现而言,pthread_t 类型的线程ID,本质上是一个进程地址空间上的一个地址。
2.3 线程终止
2.3.1 终止线程的三种方法
如果需要只终止某个线程而不终止整个进程,可以有三个方法:
- 从线程函数中return,这种方法对主线程不适用,从main函数中调用return相当于调用exit
- 线程可以调用pthread_exit进行终止自己
- 一个线程可以调用pthread_cancel终止同一个进程的另一个线程
2.3.2 pthread_exit函数
函数的原型:
函数的功能:
线程终止
函数的参数:
value_str:value_str不要指向一个局部变量,和return的变量是一样的
需要注意:pthread_exit或者return返回的指针所指向的内存单元必须是全局的或者是用malloc分配的,不能在线程函数的栈上分配的。因为当其他线程达到这个返回指针时,线程函数已经退出了。
2.3.3 pthread_cancel函数
函数的原型:
函数的功能:
取消一个执行中的线程
函数的参数:
- thread:线程ID
函数的返回值:
- 成功返回0
- 失败返回错误码
2.4 分类线程
2.4.1 可不可以不join线程,让线程执行完就自动退出?可以!!!!!
- 默认情况下,新创建的线程是joinable的,线程退出后,需要对其进行pthread_join操作,否则无法释放资源,从而造成系统泄漏。
- 如果不关心线程的返回值,join是一种负担,这个时候,我们可以告诉系统,当线程退出时,自动释放线程资源。
- 如果一个线程被分离,线程的工作状态属于分离状态,不需要也不能被join的
2.4.2 函数介绍
函数的原型:
函数的功能:
线程的分离
函数的参数:
thread:线程的ID
三、C++11多线程
在C++中不使用Linux这一套,代码如下:要记得加pthread库,否则会报错!!!!!!!
#include <iostream>
#include <pthread.h>
#include <sys/types.h>
#include <stdlib.h>
#include <thread>
#include <string>
#include <cstring>
#include <unistd.h>
#include <vector>
void threadrun(int num)
{
while (num)
{
std::cout << "thread - 1" << " is running" << std::endl;
num--;
sleep(1);
}
}
int main()
{
std::thread mythread(threadrun, 10);
while (true)
{
std::cout << "main thread is running" << std::endl;
sleep(1);
}
mythread.join();
return 0;
}