【Linux】线程控制

一、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得到的终止状态是不同的,总结如下:

  1. 如果thread线程通过return返回,value_ptr所指向的单元里存放的是thread线程函数的返回值
  2. 如果thread线程被别的线程调用pthread_cancel异常终止,value_ptr所指向的单元里存放的是常数PTHREAD_CANCELED
  3. 如果thread线程是自己调用pthread_exit终止的,value_ptr所指向的单元存放的是传给pthread_exit的参数
  4. 如果对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;
}

                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 

相关推荐

最近更新

  1. TCP协议是安全的吗?

    2024-06-10 23:36:04       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-06-10 23:36:04       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-06-10 23:36:04       18 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-06-10 23:36:04       20 阅读

热门阅读

  1. 常用位算法

    2024-06-10 23:36:04       12 阅读
  2. Linux系统sort排序与uniq去重

    2024-06-10 23:36:04       12 阅读
  3. MySQL 存储函数及调用

    2024-06-10 23:36:04       13 阅读
  4. 【源码】Spring Data JPA原理解析之事务执行原理

    2024-06-10 23:36:04       10 阅读
  5. 【软考的系统分析师的考题考点解析2025】

    2024-06-10 23:36:04       13 阅读
  6. 非比较排序之计数排序

    2024-06-10 23:36:04       8 阅读
  7. 设计模式七大原则-单一职责原则SingleResponsibility

    2024-06-10 23:36:04       11 阅读
  8. splice()、slice()、split()三种方法的区别

    2024-06-10 23:36:04       9 阅读
  9. Python怎么分开画图:深入探索与实战应用

    2024-06-10 23:36:04       9 阅读