【Yolov8部署】 VS2019+opencv-dnn CPU环境下部署目标检测模型
文章目录
- 前言
- 一、导出yolov8模型为onnx文件
- 二、VS2019配置及opencv环境配置
- 三、opencv部署
- 总结
前言
本文主要研究场景为工业场景下,在工控机与工业相机环境中运行的视觉缺陷检测系统,因此本文主要目的为实现c++环境下,将yolov8已训练好的检测模型部署在opencv中通过cpu进行检测运算
一、导出yolov8模型为onnx文件
Yolo官方支持onnx的使用,直接使用yolo的官方库即可
配置好训练权重文件路径,输出参数按自己需求查手册配置
from ultralytics import YOLO# Load a model
#model = YOLO("yolov8n.pt") # Load an official model
model = YOLO(r"D:\deep_learning\YOLOv8.2\runs\train\exp9\weights\best.pt") # Load a custom trained model# Export the model
success = model.export(format="onnx", opset=11, simplify=True)
输出成功的文件会在权重文件同目录下

这里生成的文件为11.6MB,过小的onnx文件可能是生成失败产生的

获取了onnx文件后接下来配置调用onnx文件的部分
二、VS2019配置及opencv环境配置
VS2019在微软官方网站上下载即可
opencv的版本有很多,但经过多次测试发现在cpu场景下要成功调用dnn模块至少需要4.8.0版本
opencv 4.5.4版本
opencv 4.5.5版本
opencv 4.7.0版本
经测试都会在读取onnx文件时发生致命错误
cv::dnn::readNetFromONNX(modelPath);
该条函数在调用时会直接抛出异常终止程序
0x00007FFC088CB699 处(位于 OpencvYoloYest.exe 中)有未经处理的异常:
Microsoft C++ 异常: cv::Exception,位于内存位置 0x0000002AF32FB5B0 处。
opencv需要在官网下载,官网提供X64的release包可以直接下载提取使用
也可通过cmake编译cuda版opencv或是x86的动态库来使用
opencv官方releases下载地址
选取Windows版下载即可,或下载Sources包使用cmake手动编译

本文仅记录采用opencv 4.8.0版本
opencv的环境配置主要分为两部分
系统变量设置:


VS2019动态库配置:
注意C++标准需要选择为ISO C++17标准

包含目录与库目录配置

在链接器的输入附加依赖项中添加opencv动态库
注意Debug版本需要将该opencv_world480.lib改为opencv_world480d.lib

跨平台或老旧工控机需要x86版本则需通过cmake手动编译win32版本
至此环境配置就完成了,接下来是库的调用,即在opencv中的部署
三、opencv部署
本文提供代码,同时也可通过yolo官方处下载代码
链接与代码如下:
YOLOv8-CPP-Inference
inference.cpp
#include "inference.h"Inference::Inference(const std::string onnxModelPath, const cv::Size modelInputShape= cv::Size(640, 640), const std::string classesTxtFile= "classes.txt", const bool runWithCuda=false)
{modelPath = onnxModelPath;modelShape = modelInputShape;classesPath = classesTxtFile;cudaEnabled = runWithCuda;loadOnnxNetwork();//loadClassesFromFile(); The classes are hard-coded for this example
}std::vector<Detection> Inference::runInference(const cv::Mat& input)
{cv::Mat modelInput = input;if (letterBoxForSquare && modelShape.width == modelShape.height)modelInput = formatToSquare(modelInput);cv::Mat blob;cv::dnn::blobFromImage(modelInput, blob, 1.0 / 255.0, modelShape, cv::Scalar(), true, false);net.setInput(blob);std::vector<cv::Mat> outputs;net.forward(outputs, net.getUnconnectedOutLayersNames());//cv::Mat cpuOutput;//outputs[0].copyTo(cpuOutput); // 将数据从 GPU 复制到 CPU 的 cv::Mat 对象中//float* data = reinterpret_cast<float*>(outputs.data); // 将数据赋值给 float* 指针int rows = outputs[0].size[1];int dimensions = outputs[0].size[2];bool yolov8 = true;// yolov5 has an output of shape (batchSize, 25200, 85) (Num classes + box[x,y,w,h] + confidence[c])// yolov8 has an output of shape (batchSize, 84, 8400) (Num classes + box[x,y,w,h])if (dimensions > rows) // Check if the shape[2] is more than shape[1] (yolov8){yolov8 = true;rows = outputs[0].size[2];dimensions = outputs[0].size[1];outputs[0] = outputs[0].reshape(1, dimensions);cv::transpose(outputs[0], outputs[0]);}//if (cv::cuda::getCudaEnabledDeviceCount() > 0) { // 检查是否启用了GPU计算// // cv::cuda::GpuMat gpuData(outputs[0]); // 将 GPU 数据包装到 cv::cuda::GpuMat 中// cv::Mat cpuData;// gpuData.download(cpuData); // 将 GPU 数据下载到 CPU 的 cv::Mat 中// float* data = (float*)cpuData.data; // 获取 CPU 上的数据指针//}//else { // 在没有启用GPU计算时,直接使用CPU内存中的数据指针// data = (float*)outputs[0].data;//}//float* data = (float*)outputs[0].data;//************************GPU和CPU的数据交换//cv::UMat umatData = outputs[0].getUMat(cv::ACCESS_READ);//cv::Mat cpuData;//umatData.copyTo(cpuData);//********************************//float* data = (float*)cpuData.data;float* data = (float*)outputs[0].data;float x_factor = modelInput.cols / modelShape.width;float y_factor = modelInput.rows / modelShape.height;std::vector<int> class_ids;std::vector<float> confidences;std::vector<cv::Rect> boxes;for (int i = 0; i < rows; ++i){if (yolov8){float* classes_scores = data + 4;cv::Mat scores(1, classes.size(), CV_32FC1, classes_scores);cv::Point class_id;double maxClassScore;minMaxLoc(scores, 0, &maxClassScore, 0, &class_id);if (maxClassScore > modelScoreThreshold){confidences.push_back(maxClassScore);class_ids.push_back(class_id.x);float x = data[0];float y = data[1];float w = data[2];float h = data[3];int left = int((x - 0.5 * w) * x_factor);int top = int((y - 0.5 * h) * y_factor);int width = int(w * x_factor);int height = int(h * y_factor);boxes.push_back(cv::Rect(left, top, width, height));}}else // yolov5{float confidence = data[4];if (confidence >= modelConfidenceThreshold){float* classes_scores = data + 5;cv::Mat scores(1, classes.size(), CV_32FC1, classes_scores);cv::Point class_id;double max_class_score;minMaxLoc(scores, 0, &max_class_score, 0, &class_id);if (max_class_score > modelScoreThreshold){confidences.push_back(confidence);class_ids.push_back(class_id.x);float x = data[0];float y = data[1];float w = data[2];float h = data[3];int left = int((x - 0.5 * w) * x_factor);int top = int((y - 0.5 * h) * y_factor);int width = int(w * x_factor);int height = int(h * y_factor);}}}data += dimensions;}std::vector<int> nms_result;cv::dnn::NMSBoxes(boxes, confidences, modelScoreThreshold, modelNMSThreshold, nms_result);std::vector<Detection> detections{};for (unsigned long i = 0; i < nms_result.size(); ++i){int idx = nms_result[i];Detection result;result.class_id = class_ids[idx];result.confidence = confidences[idx];std::random_device rd;std::mt19937 gen(rd());std::uniform_int_distribution<int> dis(100, 255);result.color = cv::Scalar(dis(gen),dis(gen),dis(gen));result.className = classes[result.class_id];result.box = boxes[idx];detections.push_back(result);}return detections;
}void Inference::loadClassesFromFile()
{std::ifstream inputFile(classesPath);if (inputFile.is_open()){std::string classLine;while (std::getline(inputFile, classLine))classes.push_back(classLine);inputFile.close();}
}void Inference::loadOnnxNetwork()
{net = cv::dnn::readNetFromONNX(modelPath);if (cudaEnabled){std::cout << "\nRunning on CUDA" << std::endl;net.setPreferableBackend(cv::dnn::DNN_BACKEND_CUDA);net.setPreferableTarget(cv::dnn::DNN_TARGET_CUDA_FP16);}else{std::cout << "\nRunning on CPU" << std::endl;net.setPreferableBackend(cv::dnn::DNN_BACKEND_OPENCV);net.setPreferableTarget(cv::dnn::DNN_TARGET_CPU);}
}cv::Mat Inference::formatToSquare(const cv::Mat& source)
{int col = source.cols;int row = source.rows;int _max = MAX(col, row);cv::Mat result = cv::Mat::zeros(_max, _max, CV_8UC3);source.copyTo(result(cv::Rect(0, 0, col, row)));return result;
}
inference.h
#ifndef INFERENCE_H
#define INFERENCE_H// Cpp native
#include <fstream>
#include <vector>
#include <string>
#include <random>// OpenCV / DNN / Inference
#include <opencv2/imgproc.hpp>
#include <opencv2/opencv.hpp>
#include <opencv2/dnn.hpp>//Detection结构体用来保存目标检测的结果实例
struct Detection
{int class_id{ 0 };//整形变量用来存储检测到的目标的类别,默认值为0std::string className{};//字符串变量用来存储检测到的目标的名称,默认值为空字符串float confidence{ 0.0 };//目标检测的置信度(即对目标存在的确定程度)。默认值为0.0。cv::Scalar color{};//OpenCV库中的Scalar类型变量,用于存储颜色信息。它可以表示RGB、BGR或灰度颜色空间中的颜色cv::Rect box{}; //cv::Rect 类型包含四个成员变量:x、y、width 和 height
};//Infrence类用来执行目标检测
class Inference
{
public://构造函数(modelInputShape是指模型的大小,默认为640,640;classesTxtFile是类别名称的文本文件路径(可选参数,默认为空字符串);runWithCuda是一个布尔值,指示是否使用CUDA加速运行(可选参数,默认为true)。Inference(std::string onnxModelPath, cv::Size modelInputShape, std::string classesTxtFile, bool runWithCuda);//公有成员函数,用于执行目标检测推断。它接受一个cv::Mat类型的输入图像,并返回一个std::vector<Detection>类型的检测结果。该函数将执行目标检测算法,将检测到的目标信息封装到Detection结构体中,并将所有检测结果存储在一个向量中。std::vector<Detection> runInference(const cv::Mat& input);//私有成员函数,用于内部操作
private://loadClassesFromFile函数从文本文件中加载类别名称void loadClassesFromFile();//loadOnnxNetwork函数加载ONNX模型void loadOnnxNetwork();//formatToSquare函数将输入图像调整为正方形形状。cv::Mat formatToSquare(const cv::Mat& source);//这些是私有成员变量std::string modelPath{};//存储模型文件路径std::string classesPath{};//类别文件路径bool cudaEnabled{};//CUDA加速的状态//字符串向量,用于存储目标检测的类别名称。默认情况下,它包含了一些通用的目标类别名称std::vector<std::string> classes{ "bright_collision", "dark_collision" };//std::vector<std::string> classes{ "person", "bicycle", "car", "motorcycle", "airplane", "bus", "train", "truck", "boat", "traffic light", "fire hydrant", "stop sign", "parking meter", "bench", "bird", "cat", "dog", "horse", "sheep", "cow", "elephant", "bear", "zebra", "giraffe", "backpack", "umbrella", "handbag", "tie", "suitcase", "frisbee", "skis", "snowboard", "sports ball", "kite", "baseball bat", "baseball glove", "skateboard", "surfboard", "tennis racket", "bottle", "wine glass", "cup", "fork", "knife", "spoon", "bowl", "banana", "apple", "sandwich", "orange", "broccoli", "carrot", "hot dog", "pizza", "donut", "cake", "chair", "couch", "potted plant", "bed", "dining table", "toilet", "tv", "laptop", "mouse", "remote", "keyboard", "cell phone", "microwave", "oven", "toaster", "sink", "refrigerator", "book", "clock", "vase", "scissors", "teddy bear", "hair drier", "toothbrush" };//这是一个OpenCV库中的Size2f类型变量,用于存储模型的输入形状(宽度和高度)。cv::Size2f modelShape{};//设置目标检测的阈值float modelConfidenceThreshold{ 0.50 };//目标置信度的阈值float modelScoreThreshold{ 0.45 };//目标得分的阈值float modelNMSThreshold{ 0.50 };//非最大抑制的阈值//布尔变量,指示是否使用letterbox技术将输入图像调整为正方形形状bool letterBoxForSquare = true;//该类封装了目标检测推断的相关操作和参数,通过调用构造函数和成员函数,你可以加载模型、执行推断,并获取目标检测的结果cv::dnn::Net net;//penCV库中的Net类型变量,用于存储加载的目标检测网络模型
};#endif // INFERENCE_H
main.cpp
#include "inference.h"
#include "opencv_yolo.h"int main(int argc, char *argv[])
{//main中调用//执行视频检测算法和处理bool runOnGPU = false;int deviceId = 0; // 指定要使用的GPU设备的索引//cv::cuda::setDevice(deviceId); //本机为amd卡,无法安装cuda所以此语句屏蔽//1. 设置你的onnx模型//注意,在这个例子中类别是硬编码的,'classes.txt'只是一个占位符。Inference inf("D:/VsEnvironment/dataSet/best.onnx", cv::Size(640, 640), "classes.txt", runOnGPU); // classes.txt 可以缺失string imgpath = "D:/VsEnvironment/dataSet/deep_screw_roi/test/images/Image_20241126164807270.bmp";cv::Mat frame = imread(imgpath);std::vector<Detection> output = inf.runInference(frame);int detections = output.size();std::cout << "Number of detections: " << detections << std::endl;for (int i = 0; i < detections; ++i){Detection detection = output[i];cv::Rect box = detection.box;cv::Scalar color = detection.color;//根据类别不同,显示不同的颜色if (detection.class_id == 0) {color = cv::Scalar(0, 255, 0); // 红色 (B, G, R)}else if (detection.class_id == 1) {color = cv::Scalar(0, 0, 255); // 红色 (B, G, R)}else {color = cv::Scalar(255, 0, 0); // 红色 (B, G, R)}// Detection boxcv::rectangle(frame, box, color, 2);// Detection box textstd::string classString = detection.className + ' ' + std::to_string(detection.confidence).substr(0, 4);cv::Size textSize = cv::getTextSize(classString, cv::FONT_HERSHEY_DUPLEX, 1, 2, 0);cv::Rect textBox(box.x, box.y - 40, textSize.width + 10, textSize.height + 20);cv::rectangle(frame, textBox, color, cv::FILLED);cv::putText(frame, classString, cv::Point(box.x + 5, box.y - 10), cv::FONT_HERSHEY_DUPLEX, 1, cv::Scalar(0, 0, 0), 2, 0);}cv::namedWindow("Inference", cv::WINDOW_NORMAL); // 创建具有可调整大小功能的窗口cv::imshow("Inference", frame); // 在窗口中显示图像cv::destroyAllWindows();}
总结
如下为成功运行例程,检测图像涉及科研项目,此处不便展示

以后还将完成VS2019+opencv+onnxruntime cpu与gpu的cuda加速环境下部署目标检测模型的测试,同时上传csdn文章
相关文章:
【Yolov8部署】 VS2019+opencv-dnn CPU环境下部署目标检测模型
文章目录 前言一、导出yolov8模型为onnx文件二、VS2019配置及opencv环境配置三、opencv部署总结 前言 本文主要研究场景为工业场景下,在工控机与工业相机环境中运行的视觉缺陷检测系统,因此本文主要目的为实现c环境下,将yolov8已训练好的检测…...
【嵌入式学习3】零散知识点
目录 1、systemctl命令 2、软链接和硬链接 软链接:类似快捷方式 硬链接 3、网络配置 域名解析 固定ip 为什么要固定ip? 如何固定? 4、网络请求与下载 5、端口(物理/虚拟) 端口分类: 端口管理与…...
软考《信息系统运行管理员》- 6.2 信息系统硬件的安全运维
硬件安全运行的概念 硬件安全运行的含义是保护支撑信息系统业务活动的信息系统硬件资产免遭自然灾害、人 为因素及各种计算机犯罪行为导致的破坏。硬件安全通常包括环境安全、设备安全和介质安全。 硬件安全运行的影响因素 硬件安全运行的影响因素主要有: (1)自然…...
3.30学习总结 Java包装类+高精度算法+查找算法
包装类: 基本数据类型对应的引用数据类型。 基本数据类型:在内存中记录的是真实的值。 八种包装类的父类都是Object类。 对象之间不能直接进行计算。 JDK5之后可以把int和integer看成一个东西,因为会进行内部优化。自动装箱和自动拆箱。 …...
请描述下你对vue生命周期的理解?在created和mounted这两个生命周期中请求数据有什么区别呢?
一、生命周期是什么 生命周期(Life Cycle)的概念应用很广泛,特别是在政治、经济、环境、技术、社会等诸多领域经常出现,其基本涵义可以通俗地理解为“从摇篮到坟墓”(Cradle-to-Grave)的整个过程在Vue中实例从创建到销毁的过程就是生命周期,即指从创建、初始化数据、编…...
3月30号
// 1.toString 返回对象的字符串表示形式Object objnew Object();String str1obj.toString();System.out.println(str1);//java.lang.Objectb4c966a// 核心逻辑: // 当我们打印一个对象的时候,底层会调用对象的toString方法,把对象变成字符串 // 然…...
Java——输入,循环,BigInteger,拷贝,排序
读取输入 打印输出到“ 标准输出流”(即控制台窗口)是一件非常容易的事情,只要 调用System.out.println 即可。然而,读取“ 标准输人流” System.in就没有那么简单了。要想通 过控制台进行输人,首先需要构造一个Scann…...
Elasticsearch客户端工具初探--kibana
1 Kibana简介 Kibana是Elastic Stack(ELK)中的可视化工具,用于对Elasticsearch中存储的数据进行搜索、分析和可视化展示。它提供了直观的Web界面,支持日志分析、业务监控、数据探索等功能,广泛应用于运维监控、安全分析…...
ollama在win10安装、使用、卸载
目录 前置: 1 下载ollama 2 安装 3 配置环境变量,设置模型存储位置 4 使用 5 卸载 前置: 1 在打算安装ollama之前,需要先检查电脑当前状态是否能使用ollama。确认条件满足再进行安装操作。 2 https://github.com/ollama/…...
查看iphone手机的使用记录-克魔实战
如何查看 iOS 设备近期的详细使用数据 在日常使用手机时,了解设备的运行状态和各项硬件的使用情况可以帮助分析耗电情况、优化应用使用方式。iOS 设备提供了一些数据记录,能够显示应用的启动和关闭时间、后台运行情况,以及应用在使用过程中调…...
[Lc5_dfs+floodfill] 简介 | 图像渲染 | 岛屿数量
目录 0.floodfill算法简介 1.图像渲染 题解 2.岛屿数量 题解 之前我们在 bfs 中有介绍过[Lc15_bfsfloodfill] 图像渲染 | 岛屿数量 | 岛屿的最大面积 | 被围绕的区域,现在我们来看看 dfs 又是如何解决的呢 0.floodfill算法简介 floodfill算法又叫洪水灌溉或者…...
AI-Sphere-Butler之如何使用腾讯云ASR语音识别服务
环境: AI-Sphere-Butler WSL2 英伟达4070ti 12G Win10 Ubuntu22.04 腾讯云ASR 问题描述: AI-Sphere-Butler之如何使用腾讯云ASR语音识别服务,本地硬件配置不高的情况,建议使用云服务商的ASR 解决方案: 1.登…...
Qwen最新多模态大模型:Qwen2.5-Omni介绍与快速入门
一、模型技术突破:重新定义多模态交互 近日,Qwen2.5-Omni正式发布了! 这是Qwen系列中全新的旗舰级端到端多模态大模型,专为全面的多模式感知设计,无缝处理包括文本、图像、音频和视频在内的各种输入,同时…...
【Golang】第十一弹------反射
🎁个人主页:星云爱编程 🔍所属专栏:【Go】 🎉欢迎大家点赞👍评论📝收藏⭐文章 长风破浪会有时,直挂云帆济沧海 目录 1.反射基本介绍 2.反射重要的函数和概念 3.反射应用场景 4.反…...
C#里使用libxl的对齐/边框/颜色
一份好的EXCEL文件,通道会有不同的颜色和边框来表示。 以便表示一些重要的信息,这样才能让人们一眼就看到需要关注的信息。 如下面所示: 要显示上面的内容,需要使用下面的例子: private void button12_Click(object sender, EventArgs e){var book = new ExcelBook();if…...
算法刷题记录——LeetCode篇(1.4) [第31~40题](持续更新)
更新时间:2025-03-29 算法题解目录汇总:算法刷题记录——题解目录汇总技术博客总目录:计算机技术系列博客——目录页 优先整理热门100及面试150,不定期持续更新,欢迎关注! 32. 最长有效括号 给你一个只包…...
软考中级-软件设计师信息安全模块考点解析
一、防火墙技术 内部网络是 安全的可信赖的外部网络是不安全的不可信赖的外部网络和内部网络之间有一个DMZ隔离区, 可以在DMZ隔离区中搭建服务:例如:WEB服务器 安全排序:内网>DMZ>外网 三个发展阶段: 包过滤防…...
【蓝桥杯】每日练习 Day 16,17
前言 接下来是这两天的题目(昨天主播打完模拟赛感觉身体被掏空所以没有写题目的总结),只有三道题。 一道并查集,一道单调栈和一道单调队列。 奶酪 分析 这是一道模板题(连通块),只讲思路。 …...
相机租赁网站基于Spring Boot SSM
目录 摘要 1. 项目背景与意义 2. 功能需求分析 3. 技术需求分析 3.1开发语言:Java13。 3.2其他技术: 4. 系统设计与实现 5. 市场分析 6. 创新点与优势 7. 预期成果与展望 摘要 随着摄影技术的普及和摄影爱好者数量的增加&#…...
树莓派超全系列文档--(14)无需交互使用raspi-config工具其一
无需交互使用raspi-config工具其一 无需交互的 raspi-configSystem optionsWireless LANAudioPasswordHostnameBoot/Auto loginNetwork at bootSplash screenPower LEDBrowser Display optionsUnderscanScreen blankingVNC resolutionComposite 文章来源: http://r…...
Linux驱动开发--IIC子系统
1.1 简介 I2C 是很常见的一种总线协议, I2C 是 NXP 公司设计的, I2C 使用两条线在主控制器和从机之间进行数据通信。一条是 SCL(串行时钟线),另外一条是 SDA(串行数据线),这两条数据线需要接上拉电阻,总线空闲的时候 …...
如何应对硬件测试覆盖率不足导致量产故障
硬件测试覆盖率不足导致的量产故障是硬件制造领域的一大痛点。要有效应对,必须从提高测试覆盖率、优化测试方案、引入风险管理机制三个方面入手。其中,优化测试方案尤为关键,应从产品设计阶段开始,通过精确的测试用例规划、详细的…...
JS数组复制方法及注意事项
在 JavaScript 中,直接赋值数组会导致引用传递(修改一个会影响另一个),因此需要创建数组的副本。以下是几种常见的浅拷贝方法: 1. 使用 slice() 方法 javascript const originalArray [1, 2, 3]; const copiedArra…...
【附JS、Python、C++题解】Leetcode面试150题(12)多数问题
一、题目 169. 多数元素 给定一个大小为 n 的数组 nums ,返回其中的多数元素。 多数元素是指在数组中出现次数大于[n/2]的元素。你可以假设数组是非空的,并且给定的数组总是存在多数元素。 示例 1: 输入:nums [3,2,3] 输出&a…...
Centos7 安装 TDengine
Centos7 安装 TDengine 1、简介 官网: https://www.taosdata.com TDengine 是一款开源、高性能、云原生的时序数据库(Time Series Database, TSDB), 它专为物联网、车联网、工业互联网、金融、IT 运维等场景优化设计。同时它还带有内建的缓…...
[特殊字符]《Curve DAO 系统学习目录》
本教程旨在系统学习 Curve DAO 项目的整体架构、核心机制、合约设计、治理逻辑与代币经济等内容,帮助开发者全面理解其设计理念及运作方式。 目录总览: 1. Curve 项目概览 • 1.1 Curve 是什么?主要解决什么问题? • 1.2 与其他…...
Pandas **Series**
以下是关于 Pandas Series 的从入门到精通的系统指南,包含核心概念、操作技巧和实战示例: 1. 入门篇:基础操作 1.1 创建Series import pandas as pd# 从列表创建 s1 pd.Series([1, 3, 5, 7, 9]) # 默认数字索引 s2 pd.Series([10, 20, 3…...
Kafka 多线程开发消费者实例
目前,计算机的硬件条件已经大大改善,即使是在普通的笔记本电脑上,多核都已经是标配了,更不用说专业的服务器了。如果跑在强劲服务器机器上的应用程序依然是单线程架构,那实在是有点暴殄天物了。不过,Kafka …...
Linux线程池实现
1.线程池实现 全部代码:whb-helloworld/113 1.唤醒线程 一个是唤醒全部线程,一个是唤醒一个线程。 void WakeUpAllThread(){LockGuard lockguard(_mutex);if (_sleepernum)_cond.Broadcast();LOG(LogLevel::INFO) << "唤醒所有的休眠线程&q…...
Linux《进程概念(上)》
在之前的Linux学习当中我们已经了解了基本的Linux指令以及基础的开发工具的使用,那么接下来我们就要开始Linux当中一个非常重要的部分的学习——进程,在此进程是我们之后Linux学习的基础,并且通过进程的学习会让我们了解更多的操作系统的相关…...
