前言
pthread 线程学习
一、线程的基本使用
1.如何引入外部库
在cmakeList中
- 第一步:引入头文件
# TODO 第一步:引入线程库的所有头文件 include_directories("D:/NDK/CoursewareCreate/ndk_06/pthreads-w32-2-9-1-release/Pre-built.2/include")
- 第二步:引入库文件(链接到库文件)
# TODO 第二步:引入线程库的库文件 链接到库 link_directories("D:/NDK/CoursewareCreate/ndk_06/pthreads-w32-2-9-1-release/Pre-built.2/lib/x86")
- 第三步:链接到目标库
# TODO 第三步:链接到目标库 target_link_libraries(ClionTestProject pthreadVC2)
- 第四步:使用时导入,头文件 pthread.h
#include <pthread.h>
- 第五步:线程库的一个坑
# 第一个坑 # TODO 第五步:解决那个宏没有定义的问题,不需要去修改源码 # CXX 代表c++, C代表c语言 # "${CMAKE_CXX_FLAGS}" CMAKE_CXX_FLAGS=AAA/BB/CC/DD.. 不破坏它的配置,在他的配置基础上 增加 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DHAVE_STRUCT_TIMESPEC") # 定义 给 增加一个 预编译的宏 HAVE_STRUCT_TIMESPEC
2.线程的基本使用
线程的定义,
#include <iostream>
#include <string>
// TODO 第四步 导入 线程的 头文件
#include <pthread.h>
using namespace std;
// void *(PTW32_CDECL *start) (void *),
void * customPThreadMethod(void * pVoid) { // 能够接受所有的类型
int result = *static_cast<int *>(pVoid);
printf("我是耗时任务,我运行了,result:%d\n", result);
for (int i = 0; i < 100; i++) {
_sleep(200);
printf("customPThreadMethod i:%d\n", (i + 1));
}
return 0; // 如果不返回,就会出现,很奇怪的问题
}
// 我们踩了第二个大坑
// 得到错误码:-1073741515 (0xC0000135)
// C:\Windows\SysWOW64 和 C:\Windows\System32 复制一份动态链接库进去
// TODO 线程的升级
void* customPThreadMethod02(void * pVoid) {
for (int i = 0; i < 100; i++) {
_sleep(200);
printf("customPThreadMethod02 i:%d\n", (i + 1));
}
return 0; // 如果不返回,就会出现,很奇怪的问题
};
int main()
{
cout << "ceshi"<<endl;
// TODO 最简单的线程初始化
/**
* PTW32_DLLPORT int PTW32_CDECL pthread_create (pthread_t * tid, 1.线程ID
const pthread_attr_t * attr, 2.线程属性
void *(PTW32_CDECL *start) (void *), 3.函数指针 ---> 异步任务 java run
void *arg); 4.函数指针的参数传递
*/
/*pthread_t pthreadID; // 线程ID
int value01 = 9988;
pthread_create(&pthreadID, 0, customPThreadMethod, &value01);
// 会等待耗时任务执行完成之后,才会打印执行下面的代码
// pthread_join(pthreadID, 0);
printf("线程执行完毕...\n");
_sleep(3000);*/
// TODO 线程的升级
pthread_t pthreadID; // 线程ID, 允许有野指针
pthread_attr_t pthreadAttr; // 线程属性,不允许有 野指针,有坑的
float value = 6.5;
// 初始化:线程属性
pthread_attr_init(&pthreadAttr);
// 什么是 分离线程 和 非分离线程 ?
// 答:非分离线程,要等到耗时任务执行全部完成以后,先执行异步任务,才会执行join后面关联的代码逻辑
// 分离线程,各玩各的,老死不相往来,所以就造成了,不管异步任务是否执行完毕,该结束就结束
// pthread_attr_setdetachstate(&pthreadAttr, PTHREAD_CREATE_DETACHED); // 开启分离线程,pthread_join效果丢失
pthread_attr_setdetachstate(&pthreadAttr, PTHREAD_CREATE_JOINABLE); // 开启非分离 和 pthread_join 关联起来了
pthread_create(&pthreadID, &pthreadAttr, customPThreadMethod02, &value);
// 先让耗时任务执行完毕后,在执行下面的代码
pthread_join(pthreadID, 0); // join是第二个参数是线程执行完后返回的结果
// 线程属性,进行回收
pthread_attr_destroy(&pthreadAttr);
return 0;
}
3.线程的高级使用
#include <iostream>
#include <string>
// TODO 第四步 导入 线程的 头文件
#include <pthread.h>
using namespace std;
// void *(PTW32_CDECL *start) (void *),
void * customPThreadMethod(void * pVoid) { // 能够接受所有的类型
int result = *static_cast<int *>(pVoid);
printf("我是耗时任务,我运行了,result:%d\n", result);
for (int i = 0; i < 100; i++) {
_sleep(200);
printf("customPThreadMethod i:%d\n", (i + 1));
}
return 0; // 如果不返回,就会出现,很奇怪的问题
}
// 我们踩了第二个大坑
// 得到错误码:-1073741515 (0xC0000135)
// C:\Windows\SysWOW64 和 C:\Windows\System32 复制一份动态链接库进去
// TODO 线程的升级
void* customPThreadMethod02(void * pVoid) {
for (int i = 0; i < 100; i++) {
_sleep(200);
printf("customPThreadMethod02 i:%d\n", (i + 1));
}
return 0; // 如果不返回,就会出现,很奇怪的问题
};
void * (*method)(void *pVoid){
};
int main()
{
cout << "ceshi"<<endl;
// TODO 最简单的线程初始化
/**
* PTW32_DLLPORT int PTW32_CDECL pthread_create (pthread_t * tid, 1.线程ID
const pthread_attr_t * attr, 2.线程属性
void *(PTW32_CDECL *start) (void *), 3.函数指针 ---> 异步任务 java run
void *arg); 4.函数指针的参数传递
*/
/*pthread_t pthreadID; // 线程ID
int value01 = 9988;
pthread_create(&pthreadID, 0, customPThreadMethod, &value01);
// 会等待耗时任务执行完成之后,才会打印执行下面的代码
// pthread_join(pthreadID, 0);
printf("线程执行完毕...\n");
_sleep(3000);*/
// TODO 线程的升级
pthread_t pthreadID; // 线程ID, 允许有野指针
pthread_attr_t pthreadAttr; // 线程属性,不允许有 野指针,有坑的
float value = 6.5;
// 初始化:线程属性
pthread_attr_init(&pthreadAttr);
// 什么是 分离线程 和 非分离线程 ?
// 答:非分离线程,要等到耗时任务执行全部完成以后,先执行异步任务,才会执行join后面关联的代码逻辑
// 分离线程,各玩各的,老死不相往来,所以就造成了,不管异步任务是否执行完毕,该结束就结束
// pthread_attr_setdetachstate(&pthreadAttr, PTHREAD_CREATE_DETACHED); // 开启分离线程,pthread_join效果丢失
pthread_attr_setdetachstate(&pthreadAttr, PTHREAD_CREATE_JOINABLE); // 开启非分离 和 pthread_join 关联起来了
pthread_create(&pthreadID, &pthreadAttr, customPThreadMethod02, &value);
// 先让耗时任务执行完毕后,在执行下面的代码
pthread_join(pthreadID, 0); // join是第二个参数是线程执行完后返回的结果
// 线程属性,进行回收
pthread_attr_destroy(&pthreadAttr);
return 0;
}
二、线程实现 生产者 消费者模式
工具类代码
// TODO 生产者 消费者 工具类
#ifndef NDK08_CODE_SAFE_QUEUE_TOOL_H
#define NDK08_CODE_SAFE_QUEUE_TOOL_H
#endif //NDK08_CODE_SAFE_QUEUE_TOOL_H
#pragma once
#include <iostream>
#include <string>
#include <pthread.h>
#include <string>
#include <queue>
using namespace std;
// 定义一个模板
template <typename T>
class SafeQueueClass
{
private:
queue<T> queue; // 定义队列
pthread_mutex_t mutex; // 定义互斥锁,为了线程安全处理 (不允许有野指针)
pthread_cond_t cond; // 定义条件变量,为了实现 等待 读取 的功能 (不允许有野指针)
public:
SafeQueueClass()
{
// 初始化 互斥锁
pthread_mutex_init(&mutex, 0);
// 初始化 条件变量
pthread_cond_init(&cond, 0);
}
~SafeQueueClass()
{
// 回收 互斥锁
pthread_mutex_destroy(&mutex);
// 回收 条件变量
pthread_cond_destroy(&cond);
}
// TODO 加入队列中(进行生产)
void add(T t)
{
// 为了保证同步的安全性,所以一进来,就锁上了
pthread_mutex_lock(&mutex);
queue.push(t);
// 告诉消费者,我这里已经生成成功
// pthread_cond_signal(&cond); // 由系统去唤醒一个线程 --- Java notify
pthread_cond_broadcast(&cond); // 唤醒通知所有线程 ---- Java notifyAll
// 解锁
pthread_mutex_unlock(&mutex);
}
// TODO 从队列中获取(进行消费)
void get(T& t) { // 引用把值传出去
// 为了保证同步的安全性,所以一进来,就锁上了
pthread_mutex_lock(&mutex);
// 用if可能有问题,可能会被系统唤醒
while (queue.empty()) { // 没有数据可以消费
// 条件变量 完成等待
pthread_cond_wait(&cond, &mutex); // 这段代码,等待之后,有可能会被系统唤醒
}
// 证明被唤醒了
t = queue.front(); // 得到 队列中的数据 仅此而已
queue.pop(); // 把数据弹出去,删除掉
// 解锁
pthread_mutex_unlock(&mutex);
}
};
线程实现 生产者 消费者模式模拟使用
#pragma once
#include <iostream>
// TODO 为了学习 生产者 消费者 的工具类代码
#include "safe_queue_tool.h"
using namespace std;
SafeQueueClass<int> sq;
// TODO 模拟演示 消费者
void * getMethod(void * pVoid)
{
while(9) {
printf("getMethod\n");
int value;
sq.get(value);
printf("消费者get 得到的数据:%d\n", value);
// 你只要输入 -1 就结束当前循环
if (-1 == value) {
printf("消费者get 全部执行完毕...");
break;
}
}
return 0;
}
// TODO 模拟演示 生产者
void * setMethod(void * pVoid) {
while (9) {
printf("setMethod\n");
int value;
printf("请输入您要的信息....\n");
cin >> value;
// 你只要输入 -1 就结束当前循环
if (-1 == value) {
sq.add(value); // 这里添加是为了,让消费者,可以获得-1,进行停止
printf("生产者set 全部执行完毕...");
break;
}
sq.add(value);
}
return 0;
}
int main() {
pthread_t pthreadGet;
pthread_create(&pthreadGet, 0, getMethod, 0);
pthread_t pthreadSet;
pthread_create(&pthreadSet, 0, setMethod, 0);
pthread_join(pthreadGet, 0);
cout << "AAAAAAAAAAAA..." << endl; // 线程任务执行结束后,才打印此语句
pthread_join(pthreadSet, 0);
cout << "main 函数执行..." << endl; // 线程任务执行结束后,才打印此语句
return 0;
}