提示:文章
文章目录
前言
前期疑问:
本文目标:
一、背景
接上文,关于多线程类的静态成员处理方法
C++多线程
五、遗留问题
6.1 问题1
就是我在想,有的线程类好像是定义了私有的成员,然后好像是使用this方法访问。
我的问题就是难道对于类中方法多线程,是可以直接使用一个成员吗?
对于多个对象,使用static会将所有对象的值都混在一起吗?
2024年6月11日19:30:18 解决这个问题
首先是对于问题:【对于多个对象,使用static会将所有对象的值都混在一起吗?】答案是是的,自己写了demo代码已经得到验证
代码如下:
main.cpp文件
#include <iostream>
#include <fstream>
#include <thread>
#include <vector>
#include <chrono>
#include <iomanip>
#include <unistd.h>
#include <mutex>
#include "FilesCounter.h"
constexpr int PATH_MAX = 256;
int g_num = 0;
std::vector<std::string> vecTime;
std::mutex mt;
//获取时间
std::string current_time_millis()
{
auto now = std::chrono::system_clock::now();
auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()) % 1000;
auto now_without_millis = std::chrono::system_clock::to_time_t(now);
std::tm now_tm = *std::localtime(&now_without_millis);
std::ostringstream oss;
oss << std::put_time(&now_tm, "%Y-%m-%d %H:%M:%S.")
<< std::setfill('0') << std::setw(3) << millis.count();
return oss.str();
}
std::string GetCurrentPath()
{
char cwd[PATH_MAX];
if (getcwd(cwd, sizeof(cwd)) != nullptr) {
std::cout << "当前工作目录是: " << cwd << std::endl;
} else {
std::cerr << "错误: 无法获取当前工作目录。" << std::endl;
}
return cwd;
}
int main()
{
std::string time = current_time_millis();
std::string mutiThreadStartTime = "mutiThread start time:" + time;
vecTime.push_back(mutiThreadStartTime);
// 文件名列表
std::vector<std::string> filenames = {"part00.txt", "part01.txt", "part02.txt", "part03.txt", "part04.txt",
"part05.txt", "part06.txt", "part07.txt", "part08.txt", "part09.txt",
"part10.txt", "part11.txt", "part12.txt", "part13.txt", "part14.txt",
"part15.txt", "part16.txt", "part17.txt", "part18.txt", "part19.txt"};
std::vector<std::string> filePaths;
std::string currentPath = GetCurrentPath();
currentPath = currentPath.substr(0, currentPath.find_last_of("/") + 1) + "data/";
for (std::string fileName: filenames) {
filePaths.push_back(currentPath + fileName);
}
FilesCounter filesCounter(filePaths);
filesCounter.FileReader();
std::chrono::seconds(2);
FilesCounter filesCounter2(filePaths);
filesCounter.FileReader();
time = current_time_millis();
std::string mutiThreadFinishTime = "mutiThread finish time:" + time;
vecTime.push_back(mutiThreadFinishTime);
//读取文件的非多线程
for (const auto &filename: filePaths) {
// std::cout << "noMutiThread: " << filename << std::endl;
std::ifstream file(filename);
std::string line;
while (std::getline(file, line)) {
}
}
time = current_time_millis();
std::string notMutiThreadFinishTime = "not mutiThread finish time:" + time;
vecTime.push_back(notMutiThreadFinishTime);
for (int i = 0; i < vecTime.size(); i++) {
std::cout << vecTime[i] << std::endl;
}
std::vector<std::thread> numThreads;
for (auto &thread: numThreads) {
thread.join();
}
std::cout << "final g_num" << g_num << std::endl;
return 0;
}
FilesCounter.h文件
#ifndef FILES_COUNTER_H
#define FILES_COUNTER_H
#include <string>
class FilesCounter
{
public:
FilesCounter(std::vector<std::string> filesPath);
virtual ~FilesCounter();
public:
int StaticCounter();
static void ReadFile(const std::string &filename);
int Counter();
void FileReader();
public:
static int m_staticCounter;
private:
int m_counter;
std::vector<std::string> m_filesPath;
};
#endif
FileCounter.h文件
#include <fstream>
#include <vector>
#include <thread>
#include <iostream>
#include "FilesCounter.h"
int FilesCounter::m_staticCounter = 0;
FilesCounter::FilesCounter(std::vector<std::string> filesPath)
{
m_filesPath = filesPath;
}
FilesCounter::~FilesCounter()
{}
// 线程函数,用于读取文件
void FilesCounter::ReadFile(const std::string &filename)
{
std::ifstream file(filename);
std::string line;
std::cout << filename << std::endl;
m_staticCounter++;
}
void FilesCounter::FileReader()
{
// 创建读取文件的线程
std::vector<std::thread> threads;
for (const auto &filename: m_filesPath) {
threads.emplace_back(ReadFile, filename);
}
// 等待所有线程完成
for (auto &thread: threads) {
thread.join();
}
std::cout << "m_staticCounter: " << m_staticCounter << std::endl;
}
打印信息
///data2/myTestProj/test_mutiThreadstaitic/data/part00.txt
///data2/myTestProj/test_mutiThreadstaitic/data/part01.txt
///data2/myTestProj/test_mutiThreadstaitic/data/part02.txt
///data2/myTestProj/test_mutiThreadstaitic/data/part03.txt
///data2/myTestProj/test_mutiThreadstaitic/data/part04.txt
///data2/myTestProj/test_mutiThreadstaitic/data/part05.txt
///data2/myTestProj/test_mutiThreadstaitic/data/part06.txt
///data2/myTestProj/test_mutiThreadstaitic/data/part07.txt
///data2/myTestProj/test_mutiThreadstaitic/data/part08.txt
///data2/myTestProj/test_mutiThreadstaitic/data/part09.txt
///data2/myTestProj/test_mutiThreadstaitic/data/part10.txt
///data2/myTestProj/test_mutiThreadstaitic/data/part11.txt
///data2/myTestProj/test_mutiThreadstaitic/data/part12.txt
///data2/myTestProj/test_mutiThreadstaitic/data/part13.txt
///data2/myTestProj/test_mutiThreadstaitic/data/part14.txt
///data2/myTestProj/test_mutiThreadstaitic/data/part15.txt
///data2/myTestProj/test_mutiThreadstaitic/data/part16.txt
///data2/myTestProj/test_mutiThreadstaitic/data/part17.txt
///data2/myTestProj/test_mutiThreadstaitic/data/part18.txt
///data2/myTestProj/test_mutiThreadstaitic/data/part19.txt
// m_staticCounter: 20
///data2/myTestProj/test_mutiThreadstaitic/data/part00.txt
///data2/myTestProj/test_mutiThreadstaitic/data/part01.txt
///data2/myTestProj/test_mutiThreadstaitic/data/part02.txt
///data2/myTestProj/test_mutiThreadstaitic/data/part03.txt
///data2/myTestProj/test_mutiThreadstaitic/data/part04.txt
///data2/myTestProj/test_mutiThreadstaitic/data/part05.txt
///data2/myTestProj/test_mutiThreadstaitic/data/part06.txt
///data2/myTestProj/test_mutiThreadstaitic/data/part07.txt
///data2/myTestProj/test_mutiThreadstaitic/data/part08.txt
///data2/myTestProj/test_mutiThreadstaitic/data/part09.txt
///data2/myTestProj/test_mutiThreadstaitic/data/part10.txt
///data2/myTestProj/test_mutiThreadstaitic/data/part11.txt
///data2/myTestProj/test_mutiThreadstaitic/data/part12.txt
///data2/myTestProj/test_mutiThreadstaitic/data/part13.txt
///data2/myTestProj/test_mutiThreadstaitic/data/part14.txt
///data2/myTestProj/test_mutiThreadstaitic/data/part15.txt
///data2/myTestProj/test_mutiThreadstaitic/data/part16.txt
///data2/myTestProj/test_mutiThreadstaitic/data/part17.txt
///data2/myTestProj/test_mutiThreadstaitic/data/part18.txt
///data2/myTestProj/test_mutiThreadstaitic/data/part19.txt
// m_staticCounter: 40
//mutiThread start time:2024-06-11 19:34:15.689
//mutiThread finish time:2024-06-11 19:34:15.692
//not mutiThread finish time:2024-06-11 19:34:16.551
//final g_num0
从结果可以看出来,对FilesCounter对象再次执行多线程读取文件的时候,类静态成员m_staticCounter的值被累加了。原因是因为类的静态成员被类对象共享。所以这边就复现了类静态成员在执行对象的多线程时就累计了。
这边怎么处理呢?我百度了一下资料,其中这篇文章我看到了解决办法C++类内多线程创建和调用成员变量的两种方式。
按照文章可以使用std::bind来处理。std::bind用法是threads.emplace_back(std::bind(&FilesCounter::ReadFileWithClass, this));
修改代码如下
main.cpp
#include <iostream>
#include <fstream>
#include <thread>
#include <vector>
#include <chrono>
#include <iomanip>
#include <unistd.h>
#include <mutex>
#include "FilesCounter.h"
constexpr int PATH_MAX = 256;
int g_num = 0;
std::vector<std::string> vecTime;
std::mutex mt;
//获取时间
std::string current_time_millis()
{
auto now = std::chrono::system_clock::now();
auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()) % 1000;
auto now_without_millis = std::chrono::system_clock::to_time_t(now);
std::tm now_tm = *std::localtime(&now_without_millis);
std::ostringstream oss;
oss << std::put_time(&now_tm, "%Y-%m-%d %H:%M:%S.")
<< std::setfill('0') << std::setw(3) << millis.count();
return oss.str();
}
std::string GetCurrentPath()
{
char cwd[PATH_MAX];
if (getcwd(cwd, sizeof(cwd)) != nullptr) {
std::cout << "当前工作目录是: " << cwd << std::endl;
} else {
std::cerr << "错误: 无法获取当前工作目录。" << std::endl;
}
return cwd;
}
int main()
{
std::string time = current_time_millis();
std::string mutiThreadStartTime = "mutiThread start time:" + time;
vecTime.push_back(mutiThreadStartTime);
// 文件名列表
std::vector<std::string> filenames = {"part00.txt", "part01.txt", "part02.txt", "part03.txt", "part04.txt",
"part05.txt", "part06.txt", "part07.txt", "part08.txt", "part09.txt",
"part10.txt", "part11.txt", "part12.txt", "part13.txt", "part14.txt",
"part15.txt", "part16.txt", "part17.txt", "part18.txt", "part19.txt"};
std::vector<std::string> filePaths;
std::string currentPath = GetCurrentPath();
currentPath = currentPath.substr(0, currentPath.find_last_of("/") + 1) + "data/";
for (std::string fileName: filenames) {
filePaths.push_back(currentPath + fileName);
}
FilesCounter filesCounter(filePaths);
filesCounter.FileReaderWithClass();
std::chrono::seconds(2);
FilesCounter filesCounter2(filePaths);
filesCounter2.FileReaderWithClass();
time = current_time_millis();
std::string mutiThreadFinishTime = "mutiThread finish time:" + time;
vecTime.push_back(mutiThreadFinishTime);
//读取文件的非多线程
for (const auto &filename: filePaths) {
// std::cout << "noMutiThread: " << filename << std::endl;
std::ifstream file(filename);
std::string line;
while (std::getline(file, line)) {
}
}
time = current_time_millis();
std::string notMutiThreadFinishTime = "not mutiThread finish time:" + time;
vecTime.push_back(notMutiThreadFinishTime);
for (int i = 0; i < vecTime.size(); i++) {
std::cout << vecTime[i] << std::endl;
}
std::vector<std::thread> numThreads;
for (auto &thread: numThreads) {
thread.join();
}
std::cout << "final g_num" << g_num << std::endl;
return 0;
}
FileCounter.cpp
#include <fstream>
#include <vector>
#include <thread>
#include <iostream>
#include "FilesCounter.h"
#include <functional>
int FilesCounter::m_staticCounter = 0;
FilesCounter::FilesCounter(std::vector<std::string> filesPath)
{
m_staticCounterWithClass = 0;
m_filesPath = filesPath;
}
FilesCounter::~FilesCounter()
{}
// 线程函数,用于读取文件
void FilesCounter::ReadFile(const std::string &filename)
{
std::ifstream file(filename);
std::string line;
std::cout << filename << std::endl;
m_staticCounter++;
}
// 线程函数,用于读取文件
void FilesCounter::ReadFileWithClass()
{
// std::ifstream file(filename);
// std::string line;
// std::cout << filename << std::endl;
m_staticCounterWithClass++;
}
void FilesCounter::FileReader()
{
// 创建读取文件的线程
std::vector<std::thread> threads;
for (const auto &filename : m_filesPath) {
threads.emplace_back(ReadFile, filename);
}
// 等待所有线程完成
for (auto &thread: threads) {
thread.join();
}
std::cout << "m_staticCounter: " << m_staticCounter << std::endl;
}
void FilesCounter::FileReaderWithClass()
{
// 创建读取文件的线程
std::vector<std::thread> threads;
for (const auto &filename : m_filesPath) {
threads.emplace_back(std::bind(&FilesCounter::ReadFileWithClass, this));
}
// 等待所有线程完成
for (auto &thread: threads) {
thread.join();
}
std::cout << "m_staticCounterWithClass: " << m_staticCounterWithClass << std::endl;
}
FileCounter.h
#ifndef FILES_COUNTER_H
#define FILES_COUNTER_H
#include <string>
class FilesCounter
{
public:
FilesCounter(std::vector<std::string> filesPath);
virtual ~FilesCounter();
public:
int StaticCounter();
static void ReadFile(const std::string &filename);
void ReadFileWithClass();
int Counter();
void FileReader();
void FileReaderWithClass();
public:
static int m_staticCounter;
int m_staticCounterWithClass;
private:
int m_counter;
std::vector<std::string> m_filesPath;
};
#endif
打印信息如下
m_staticCounterWithClass: 20
m_staticCounterWithClass: 20
mutiThread start time:2024-06-11 20:04:57.566
mutiThread finish time:2024-06-11 20:04:57.569
not mutiThread finish time:2024-06-11 20:04:58.406
final g_num0
实现了对象多线程时各个对象数据不干扰。
这边有个遗留问题就是怎么将带参的线程函数使用std::bind绑定?
6.2 问题2 为什么多线程可以提升速度
之前一直在疑惑为什么多线程可以提升效率。刚才豁然开朗。
比如之前一个主线程跑,线程一直被阻塞着,一次执行。在某一个时刻执行某一段计算,这个时候这段计算只会占用某一段堆栈资源,并没有占用cpu所有资源。这时候开辟多个线程,同时跑好几个线程,就可以并行运算提升效率。当然这个并行对于单核的cpu来说其实还是串行。
那么这时候我又产生一个疑问。虽然有足量资源供多个线程使用,但是当cpu计算时,为什么还有时间切换到其他线程,难道cpu在执行计算的时候还有时间是空闲的?所以这个疑问就是cpu为什么能切换线程,不是一直都很忙吗?
总结
未完待续