无法从超过5个线程的c ++并发写入文件

时间:2016-03-31 12:22:12

标签: c++ multithreading

我在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;
}

FileSearcher.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 &);
};

FileSearcher.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;
}

1 个答案:

答案 0 :(得分:0)

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

调用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();
        }
    });