【问题标题】:Cannot write concurrently to file from more than 5 threads c++无法从超过5个线程c ++同时写入文件
【发布时间】:2016-07-19 21:44:06
【问题描述】:

我正在一个 for 循环中启动 20 个线程来写入这样的文件:

for (FileSearcher & searcher : searchers)
{
    searcher.start([&sf](std::string & fileName, int & id)
    {
        for (int i = 1; i < 100; i++)
        {
            //sf << "T " << id << " " << fileName << i << "\n";
            std::string msg("T ");
            msg += std::to_string(id);
            msg += " ";
            msg += fileName;
            msg += std::to_string(i);;
            msg += "\n";
            sf.writeLine(msg);
        }
    });

searcher.start() 函数启动线程,该线程将执行作为参数传递的 lambda。 sf 对象拥有由线程写入的实际文件和一个由 sf.writeLine() 中的 std::lock_guard 锁定的互斥锁。

问题在于只有 5 个线程同时写入文件,其中一个线程完成工作后,下一个线程开始与其他 4 个尚未完成的线程同时写入。以此类推,直到所有 20 个线程都完成。

为什么会这样?不是应该所有 20 个线程同时写入文件,而不是一次写入 5 个吗?

完整代码如下:

主要:

#include <iostream>
#include "FileSearcher.h"
#include "SafeFileStream.h"

int main()
{
SafeFileStream sf("D:\\x.txt");

//sf.writeLine("hellow");
FileSearcher searchers[20];

for (int j = 0; j < 20; j++)
{
    searchers[j].setFileName("Some file name.txt");
    searchers[j].setId(j);
}

for (FileSearcher & searcher : searchers)
{
    searcher.start([&sf](std::string & fileName, int & id)
    {
        for (int i = 1; i < 100; i++)
        {
            //sf << "T " << id << " " << fileName << i << "\n";
            std::string msg("T ");
            msg += std::to_string(id);
            msg += " ";
            msg += fileName;
            msg += std::to_string(i);;
            msg += "\n";
            sf.writeLine(msg);
        }
    });
}


for (FileSearcher & searcher : searchers)
{
    searcher.join();
}

return 0;
}

文件搜索器.h

#include <thread>
#include <string>
#include <functional>
#include <fstream>

class FileSearcher {

private:
   std::unique_ptr<std::thread> searcher;
   std::string fileName;
   std::ifstream inputFile;
   int id;

public:
   FileSearcher();
   FileSearcher(const int &, const std::string &);
   ~FileSearcher();

   void start(std::function<void(std::string &, int &)>);
   void join();
   void setFileName(const std::string &);
   void setId(const int &);
};

文件搜索器.cpp

#include "FileSearcher.h"

FileSearcher::FileSearcher() {}

FileSearcher::FileSearcher(const int & id, const std::string & fileName)
{
    this->fileName = fileName;
    this->id = id;
}

FileSearcher::~FileSearcher()
{
    if (this->searcher->joinable())
    {
        this->searcher->join();
    }
}

void FileSearcher::setFileName(const std::string & fileName)
{
    this->fileName = fileName;
}

void FileSearcher::setId(const int & id)
{
    this->id = id;
}

void FileSearcher::start(std::function<void(std::string &, int & id)>   searchingMethod)
{
    this->inputFile.open(this->fileName);
    this->searcher = std::unique_ptr<std::thread>(new    std::thread(searchingMethod, std::ref(this->fileName), std::ref(this->id)));
}

void FileSearcher::join() 
{
   if (this->searcher->joinable())
   {
       this->searcher->join();
   }
}

SafeFileStream.h

#include <fstream>
#include <string>
#include <mutex>
#include <iostream>

class SafeFileStream
{
private:
    std::ofstream outputFile;
    std::string fileName;
    std::mutex mu;
    std::unique_ptr<std::lock_guard<std::mutex>> lockGuard;
    std::unique_lock<std::mutex> uniqueLocker;
    std::unique_ptr<std::thread> safeQueueHandler;

public:
    SafeFileStream(std::string);
    SafeFileStream(SafeFileStream&&);
    ~SafeFileStream();
    template <typename T> void writeLine(T);
    template <typename T> void write(T msg);
};

SafeFileStream::SafeFileStream(std::string fileName) : fileName(fileName)
{
    this->outputFile.open(fileName);
}

SafeFileStream::SafeFileStream(SafeFileStream&& sfFileStream)
{
    this->fileName = sfFileStream.fileName;
}

SafeFileStream::~SafeFileStream()
{
    this->outputFile.close();
}

template <typename T> void SafeFileStream::writeLine(T msg)
{
    std::lock_guard<std::mutex> lockGuard(this->mu);
    this->outputFile << msg << "\n";
}

template <typename T> void SafeFileStream::write(T msg)
{
    std::lock_guard<std::mutex> lockGuard(this->mu);
    this->outputFile << msg;
}

【问题讨论】:

  • 你只有 5 个核心?
  • 定义“只有 5 个线程同时写入文件”。锁似乎只允许一个线程在任何给定时间写入文件。
  • @Siyuan Ren 我有4核
  • @Sam Varshavchik “只有 5 个线程同时写入文件” - 我的意思是 5 个线程同时写入文件。其他15个是被动的。如果其中一个活动线程完成写入,则其中一个被动线程激活并开始与其他 4 个活动线程同时写入。
  • 据我所知,该标准不保证线程调度中的“公平性”,或者等待互斥体的线程将以任何特定顺序获得它的所有权。

标签: c++ multithreading


【解决方案1】:

您无法确定上下文切换的时间。 这取决于操作系统。 但您可以“询问”上下文切换到操作系统。

调用 std::this_thread::yield(); sf.writeLine(msg) 之后;

for (FileSearcher & searcher : searchers)
{
    searcher.start([&sf](std::string & fileName, int & id)
    {
        for (int i = 1; i < 100; i++)
        {
            //sf << "T " << id << " " << fileName << i << "\n";
            std::string msg("T ");
            msg += std::to_string(id);
            msg += " ";
            msg += fileName;
            msg += std::to_string(i);;
            msg += "\n";
            sf.writeLine(msg);
            std::this_thread::yield();
        }
    });

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-30
    • 1970-01-01
    相关资源
    最近更新 更多