C++多线程对于类的静态成员处理

提示:文章

文章目录

前言

前期疑问:
本文目标:


一、背景

接上文,关于多线程类的静态成员处理方法
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为什么能切换线程,不是一直都很忙吗?


总结

未完待续

相关推荐

  1. C++线对于静态成员处理

    2024-06-12 05:50:03       12 阅读
  2. C#线与函数对象实例

    2024-06-12 05:50:03       5 阅读
  3. C++线之通过成员函数作为线入口

    2024-06-12 05:50:03       32 阅读
  4. Qt线

    2024-06-12 05:50:03       14 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-06-12 05:50:03       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-06-12 05:50:03       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-06-12 05:50:03       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-06-12 05:50:03       18 阅读

热门阅读

  1. 几句话明白什么是Kubernetes Operator?

    2024-06-12 05:50:03       9 阅读
  2. 计算广告读书杂记-待整理

    2024-06-12 05:50:03       8 阅读
  3. 学习分享-tryLock和 lock的区别

    2024-06-12 05:50:03       9 阅读
  4. 643.子数组最大平均数

    2024-06-12 05:50:03       7 阅读
  5. 【Git】的基本概念和使用方式

    2024-06-12 05:50:03       7 阅读
  6. ls: 无法访问目录 输入/输出错误

    2024-06-12 05:50:03       5 阅读