【C++ 语言】线程 ( 线程创建方法 | 线程标识符 | 线程属性 | 线程属性初始化 | 线程属性销毁 | 分离线程 | 线程调度策略 | 线程优先级 | 线程等待 )(二)

简介: 【C++ 语言】线程 ( 线程创建方法 | 线程标识符 | 线程属性 | 线程属性初始化 | 线程属性销毁 | 分离线程 | 线程调度策略 | 线程优先级 | 线程等待 )(二)

VI 线程属性 2 ( 线程调度策略 )


该功能在 Android , Linux 上可以使用 , 在 Visual Studio 中暂时无法测试


1. 线程调度策略 : 线程是需要抢占 CPU 资源进行执行的 , 调度策略就是设置抢占 CPU 的策略 ;


2. SCHED_FIFO 策略 :


① 调度机制 : 先创建的线程先执行 , CPU 一旦占用则一直占用 ;

② CPU 资源释放时机 : 当有更高优先级的任务出现或线程执行完毕 , CPU 资源才会释放 ;

③ 串行执行 : 如果两个线程都是 SCHED_FIFO 策略 , 并且优先级一样 , 那么两个线程一起执行的话 , 要先后执行 , 无法同时执行;

3. SCHED_RR 策略 :


① 调度机制 : 时间片轮转 , 系统为不同的线程分配不同的时间段 , 指定的线程只有在指定的时间段内才能使用 CPU 资源 ;

② 并行执行 : 如果两个线程都是 SCHED_RR 策略 , 并且优先级一样 , 那么两个线程一起执行的话 , 两个线程同时执行 ;

4. 调度策略设置方法 :


① 函数原型 : int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy);

② 参数 1 ( pthread_attr_t *attr ) : 线程属性对象 ;

③ 参数 2 ( int policy ) : 调度策略 ;



VII 线程属性 3 ( 线程优先级设置 )


该功能在 Android , Linux 上可以使用 , 在 Visual Studio 中暂时无法测试


1. 线程优先级 : 优先级是一个数值 , 数值越大 , 优先级越高 , 系统在进行线程调度时 , 优先给优先级高的线程分配资源 , 优先级高的先执行 ;


2. 线程优先级类型 : 优先级是 sched_param 结构体变量 , 在 sched_param 结构体中只有一个成员sched_priority ;


struct sched_param {
    int sched_priority;
  };


3. 优先级取值范围 : 该范围与调度策略有关 , 可以获取该调度策略优先级的最大最小值 ;


① 获取 SCHED_FIFO 策略的最大优先级 :

//获取 SCHED_FIFO 策略的最大优先级
  int max_priority_of_fifo = sched_get_priority_max(SCHED_FIFO);


② 获取 SCHED_FIFO 策略的最小优先级 :

//获取 SCHED_FIFO 策略的最小优先级
  int min_priority_of_fifo = sched_get_priority_min(SCHED_FIFO);


4. 设置线程优先级代码示例 :


//获取 SCHED_FIFO 策略的最大优先级
  int max_priority_of_fifo = sched_get_priority_max(SCHED_FIFO);
  //获取 SCHED_FIFO 策略的最小优先级
  int min_priority_of_fifo = sched_get_priority_min(SCHED_FIFO);
  //声明调度参数结构体
  sched_param param;
  //设置调度参数结构体的 sched_priority 成员
  param.sched_priority = max_priority_of_fifo;
  //设置线程优先级
  pthread_attr_setschedparam(&attribute, &param);




VIII 线程等待


1. 线程等待方法 :


① 函数作用 : 等待线程结束 , 用于线程间同步操作 ;

② 函数原型 : int pthread_join(pthread_t thread, void **retval); ;

③ 参数 1 ( pthread_t thread ) : 线程标识符 , 要等待哪个线程结束 ;

④ 参数 2 ( void **retval ) : 被等待线程的返回值 ;

2. 代码示例 :


//pthread_join : 等待线程结束
  //  等线程执行完毕后 , 在执行下面的内容
  pthread_join(pid, 0);




IX 互斥锁


【C++ 语言】pthread_mutex_t 互斥锁




X 线程代码示例


1. 代码示例 :


// 005_Thread.cpp: 定义应用程序的入口点。
//
#include "005_Thread.h"
#include <pthread.h>
//引入队列的头文件
#include <queue>
using namespace std;
/*
  定义线程中要执行的方法
  将该函数的指针作为线程创建方法 pthread_create 的第三个参数
  C++ 中规定线程执行函数的函数指针类型是 void *(PTW32_CDECL *start) (void *)
*/
void* pthread_function(void* args) {
  //延迟 100 ms 执行
  //_sleep(100);
  //指针类型转换 : 将 void* 转为 char*
  //  使用 static_cast 类型转换标识符
  char* hello = static_cast<char*>(args);
  //打印参数
  cout << "pthread_function 线程方法 执行 参数 : " << hello << endl;
  return 0;
}
/*
  互斥锁 :
  声明 : 先声明互斥锁
  初始化 : 在进行初始化操作
  销毁 : 使用完毕后 , 要将该互斥锁销毁
*/
pthread_mutex_t mutex_t;
//声明一个队列变量
//  该变量是全局变量
//  该变量要在不同的线程中访问 , 用于展示线程同步
queue<int> que;
/*
  操作线程方法 : 参数和返回值都是 void* 类型
  互斥锁使用 : 多个线程对一个队列进行操作 , 
  需要使用互斥锁将该队列锁起来 , pthread_mutex_lock
  使用完毕后在进行解锁 , pthread_mutex_unlock
  该类型的锁与 Java 中的 synchronized 关键字一样 , 属于悲观锁
  其作用是通过 mutex 互斥锁 , 将上锁与解锁之间的代码进行同步 
*/
void* queue_thread_fun(void* args) {
  //先用互斥锁上锁
  pthread_mutex_lock(&mutex_t);
  if (!que.empty()) {
  //打印队列中的第一个元素
  printf("获取 que 队列第一个数据 : %d\n", que.front());
  //将队列首元素弹出
  que.pop();
  }
  else {
  printf("获取 que 队列为空\n");
  }
  //操作完毕后, 解锁
  pthread_mutex_unlock(&mutex_t);
  return 0;
}
/*
  如果 8 个线程同时读取队列中的信息 , 会出现程序崩溃
  在多线程环境下 , 对队列进 queue_thread 行操作 , queue_thread 是线程不安全的
  这里需要加锁 , 进行 线程同步的操作
*/
int main()
{
  //初始化互斥锁
  pthread_mutex_init(&mutex_t, 0);
  //向其中加入 5 个int数据
  for (size_t i = 0; i < 5; i++) {
  que.push(i);
  cout << "放入数据 : " << i << endl;
  }
  //创建多个线程操作 queue_thread 队列
  pthread_t pids[8];
  for (size_t i = 0; i < 8; i++) {
  //创建线程
  pthread_create(&pids[i], 0, queue_thread_fun, 0);
  }
  //销毁互斥锁
  pthread_mutex_destroy(&mutex_t);
  return 0;
}
int main2()
{
  cout << "Hello CMake。" << endl;
  // I. 测试 POSIX 线程方法
  pthread_self();
  // II 
  //线程标识符 , 这里需要传入指针 , 因此这里使用 & 取地址符获取其地址当做指针变量
  pthread_t pid;
  char* hello = "Hello Thread";
  /*
  线程属性结构体变量
    该线程属性需要先进行初始化和销毁;
    线程属性初始化方法 : int pthread_attr_init(pthread_attr_t *attr);
    线程属性销毁方法 : int pthread_attr_destroy(pthread_attr_t *attr);
  线程属性类型定义 : typedef struct pthread_attr_t_ * pthread_attr_t;
    pthread_attr_t 其本质是一个指针 ; 
    pthread_attr_t attribute 声明后 , 该指针是野指针 , 需要将其设置为 0 ;
  */
  pthread_attr_t attribute = 0;
  //初始化线程属性, 此处的参数是指针的指针 , 该指针指向 0 地址 ; 
  //  初始化时 , 肯定要创建一个有实际意义的线程属性结构体 , 将 attribute 二维指针 指向结构体指针
  //  指向指针的指针意义 : 在传递时可以在函数内部修改指针指向的地址 ; 
  //初始化线程属性时 , 对属性进行了默认配置 ;
  pthread_attr_init(&attribute);
  //常用属性 1 : 
  //非分离线程 ;
  //  线程创建后 , 默认是非分离线程 ; 
  //  创建线程后 , 线程执行 , 如果调用 pthread_join 函数 , 其作用是等待 pthread_function 线程函数执行完毕 ; 
  //  非分离线程允许在其它线程中 , 来等待另外线程执行完毕 ; 
  //分离线程 : 
  //  不能被其它线程操作 , 如调用 pthread_join 函数 , 无法等待该分离线程执行完毕 ; 
  /*
  设置线程属性为 分离线程
    如果没有设置分离线程时 , 先执行完线程内容 , 等待线程执行完毕后 , 才执行 pthread_join 后的代码
    如果设置了分离线程属性 , pthread_join 等待线程执行完毕是无效的 , 主线程会继续向后执行 , 
    不会等待线程执行完毕 
    因此打印出的内容是 先打印 "线程执行完毕" , 然后才打印线程方法中的内容
  不经常使用 : 一般情况下是不经常将线程设置为分离线程 , 如果设置了 , 那么该线程就无法进行控制
  */
  pthread_attr_setdetachstate(&attribute, PTHREAD_CREATE_DETACHED);
  /*
  常用属性 2 : 线程的调度策略
  该功能在 Android , Linux 上可以使用 , 在 Visual Studio 中暂时无法测试
  线程是需要抢占 CPU 资源进行执行的 , 调度策略就是设置抢占 CPU 的策略
  调度策略 : 
    SCHED_FIFO 策略 : 先创建的线程先执行 , CPU 一旦占用则一直占用 , 
    CPU 资源释放时机 : 当有更高优先级的任务出现或线程执行完毕 , CPU 资源才会释放
    串行执行 : 如果两个线程都是 SCHED_FIFO 策略 , 并且优先级一样 , 那么两个线程一起执行的话 , 
      要先后执行 , 无法同时执行; 
    SCHED_RR 策略 : 时间片轮转 , 系统为不同的线程分配不同的时间段 , 
    指定的线程只有在指定的时间段内才能使用 CPU 资源
    并行执行 : 如果两个线程都是 SCHED_RR 策略 , 并且优先级一样 , 
      那么两个线程一起执行的话 , 两个线程同时执行 
  调度策略设置方法 : 
    函数原型 : int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy);
    参数 1 ( pthread_attr_t *attr ) : 线程属性对象
    参数 2 ( int policy ) : 调度策略  
  */
  /*
  常用属性 3 : 优先级设置
  该功能在 Android , Linux 上可以使用 , 在 Visual Studio 中暂时无法测试
  优先级是一个数值 , 数值越大 , 优先级越高 , 系统在进行线程调度时 , 
    优先给优先级高的线程分配资源 , 优先级高的先执行 ;   
  优先级是 sched_param 结构体变量 , 在 sched_param 结构体中只有一个成员sched_priority ; 
  struct sched_param {
    int sched_priority;
  };
  优先级设置方法 : pthread_attr_setschedparam
  优先级取值范围 : 该范围与调度策略有关 , 可以获取该调度策略优先级的最大最小值
    下面有获取 SCHED_FIFO 的最高和最低优先级取值
  */
  //获取 SCHED_FIFO 策略的最大优先级
  int max_priority_of_fifo = sched_get_priority_max(SCHED_FIFO);
  //获取 SCHED_FIFO 策略的最小优先级
  int min_priority_of_fifo = sched_get_priority_min(SCHED_FIFO);
  //声明调度参数结构体
  sched_param param;
  //设置调度参数结构体的 sched_priority 成员
  param.sched_priority = max_priority_of_fifo;
  //设置线程优先级
  pthread_attr_setschedparam(&attribute, &param);
  /*
  线程创建方法函数原型 : 
  int pthread_create(
    pthread_t *tidp, 
    const pthread_attr_t *attr, 
    (void*)(*start_rtn)(void*), 
    void *arg);
  该方法需要提供四个参数 ;
    参数 1 ( pthread_t *tidp ) :线程标识符指针 , 该指针指向线程标识符 ;
    参数 2 ( const pthread_attr_t *attr ) : 线程属性指针 ;
    参数 3 ( (void*)(*start_rtn)(void*) ) : 线程运行函数指针 , start_rtn 是一个函数指针 , 
    其参数和返回值类型是 void* 类型
    参数 4 ( void *arg ) : 参数 3 中的线程运行函数的参数 ;
  返回值 :
    线程创建成功 , 返回 0 ;
    线程创建失败 , 返回 错误代码 ;
  关于函数指针参数 : C++ 中函数指针类型是 void *(PTW32_CDECL *start) (void *) ,
    函数的参数类型是 void* 指针
    函数的返回值类型 void* 指针
  函数多参数方案 : 如果线程执行的函数有多个参数 , 可以使用结构体 , 类进行封装
  线程属性 : 创建线程时 , 给线程指定属性 pthread_attr_t 是结构体类型
  */
  //函数指针 函数名 和 &函数名 都可以作为函数指针
  pthread_create(&pid , &attribute, pthread_function, hello);
  //pthread_join : 等待线程结束
  //  等线程执行完毕后 , 在执行下面的内容
  pthread_join(pid, 0);
  cout << " 线程执行完毕 " << endl;
  //销毁线程属性
  pthread_attr_destroy(&attribute);
  return 0;
}





2. 执行结果 :


放入数据 : 0
放入数据 : 1
放入数据 : 2
放入数据 : 3
放入数据 : 4
获取 que 队列第一个数据 : 0
获取 que 队列第一个数据 : 1
获取 que 队列第一个数据 : 2
获取 que 队列第一个数据 : 3
获取 que 队列第一个数据 : 4
D:\002_Project\006_Visual_Studio\005_Thread\out\build\x64-Debug\005_Thread\005_Thread.exe (进程 1852)已退出,返回代码为: 0。
若要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
按任意键关闭此窗口...



目录
相关文章
|
10天前
|
Java 开发者
在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口
【10月更文挑战第20天】在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口。本文揭示了这两种方式的微妙差异和潜在陷阱,帮助你更好地理解和选择适合项目需求的线程创建方式。
12 3
|
10天前
|
Java 开发者
在Java多线程编程中,选择合适的线程创建方法至关重要
【10月更文挑战第20天】在Java多线程编程中,选择合适的线程创建方法至关重要。本文通过案例分析,探讨了继承Thread类和实现Runnable接口两种方法的优缺点及适用场景,帮助开发者做出明智的选择。
10 2
|
10天前
|
安全 Java
Java多线程通信新解:本文通过生产者-消费者模型案例,深入解析wait()、notify()、notifyAll()方法的实用技巧
【10月更文挑战第20天】Java多线程通信新解:本文通过生产者-消费者模型案例,深入解析wait()、notify()、notifyAll()方法的实用技巧,包括避免在循环外调用wait()、优先使用notifyAll()、确保线程安全及处理InterruptedException等,帮助读者更好地掌握这些方法的应用。
11 1
|
10天前
|
Java 开发者
Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点
【10月更文挑战第20天】Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点,重点解析为何实现Runnable接口更具灵活性、资源共享及易于管理的优势。
22 1
|
10天前
|
Java
在Java多线程编程中,`wait()`和`notify()`方法的相遇如同一场奇妙的邂逅
在Java多线程编程中,`wait()`和`notify()`方法的相遇如同一场奇妙的邂逅。它们用于线程间通信,使线程能够协作完成任务。通过这些方法,生产者和消费者线程可以高效地管理共享资源,确保程序的有序运行。正确使用这些方法需要遵循同步规则,避免虚假唤醒等问题。示例代码展示了如何在生产者-消费者模型中使用`wait()`和`notify()`。
16 1
|
10天前
|
安全 Java 开发者
Java多线程中的`wait()`、`notify()`和`notifyAll()`方法,探讨了它们在实现线程间通信和同步中的关键作用
本文深入解析了Java多线程中的`wait()`、`notify()`和`notifyAll()`方法,探讨了它们在实现线程间通信和同步中的关键作用。通过示例代码展示了如何正确使用这些方法,并分享了最佳实践,帮助开发者避免常见陷阱,提高多线程程序的稳定性和效率。
22 1
|
10天前
|
Java
在Java多线程编程中,`wait()` 和 `notify()/notifyAll()` 方法是线程间通信的核心机制。
在Java多线程编程中,`wait()` 和 `notify()/notifyAll()` 方法是线程间通信的核心机制。它们通过基于锁的方式,使线程在条件不满足时进入休眠状态,并在条件成立时被唤醒,从而有效解决数据一致性和同步问题。本文通过对比其他通信机制,展示了 `wait()` 和 `notify()` 的优势,并通过生产者-消费者模型的示例代码,详细说明了其使用方法和重要性。
18 1
|
15天前
|
监控 Java
在实际应用中选择线程异常捕获方法的考量
【10月更文挑战第15天】选择最适合的线程异常捕获方法需要综合考虑多种因素。没有一种方法是绝对最优的,需要根据具体情况进行权衡和选择。在实际应用中,还需要不断地实践和总结经验,以提高异常处理的效果和程序的稳定性。
17 3
|
15天前
|
监控 Java
捕获线程执行异常的多种方法
【10月更文挑战第15天】捕获线程执行异常的方法多种多样,每种方法都有其特点和适用场景。在实际开发中,需要根据具体情况选择合适的方法或结合多种方法来实现全面有效的线程异常捕获。这有助于提高程序的健壮性和稳定性,减少因线程异常带来的潜在风险。
11 1
|
19天前
|
算法 C++
2022年第十三届蓝桥杯大赛C/C++语言B组省赛题解
2022年第十三届蓝桥杯大赛C/C++语言B组省赛题解
22 5

相关实验场景

更多