這是我為輸出到檔案、資料庫等的輸出驅動程式的代碼模型。在 main 的陣列中,如果我有兩個相同子型別的物件,則代碼在第二次呼叫ShutDown(). 但是,如果我有兩個不同子型別的物件,它不會停止,正確退出程式。我不知道是什么導致了這個問題。
#include <iostream>
#include <fstream>
#include <list>
#include <thread>
#include <condition_variable>
using namespace std;
class Parent
{
public:
Parent();
virtual ~Parent() = default;
virtual void ShutDown() = 0;
void Push(int aInt);
virtual void Write(int aInt) = 0;
bool IsEmpty() { return mList.empty(); }
protected:
std::list<int> Pop();
void WriteWithThread();
std::list<int> mList;
std::thread mOutputThread;
std::condition_variable mCV;
std::mutex mMutex;
std::atomic_bool mProgramRunning{ true };
std::atomic_bool mThreadRunning{ false };
};
Parent::Parent()
{
mOutputThread = std::move(std::thread(&Parent::WriteWithThread, this));
}
void Parent::Push(int aInt)
{
std::unique_lock<std::mutex> lock(mMutex);
mList.emplace_back(std::move(aInt));
lock.unlock();
mCV.notify_one();
}
std::list<int> Parent::Pop()
{
std::unique_lock<std::mutex> lock(mMutex);
mCV.wait(lock, [&] {return !mList.empty(); });
std::list<int> removed;
removed.splice(removed.begin(), mList, mList.begin());
return removed;
}
void Parent::WriteWithThread()
{
mThreadRunning = true;
while (mProgramRunning || !mList.empty())
{
Write(Pop().front());
}
mThreadRunning = false;
}
class ChildCout : public Parent
{
public:
ChildCout() = default;
void ShutDown() override;
void Write(int aInt) override;
};
void ChildCout::ShutDown()
{
mProgramRunning = false;
if (mOutputThread.joinable())
{
mOutputThread.join();
}
std::cout << "Shutdown Complete"<< std::endl;
}
void ChildCout::Write(int aInt)
{
std::cout << "Inserting number: " << aInt << std::endl;
}
class ChildFile : public Parent
{
public:
ChildFile(std::string aFile);
void ShutDown() override;
void Write(int aInt) override;
private:
std::fstream mFS;
};
ChildFile::ChildFile(std::string aFile):Parent()
{
mFS.open(aFile);
}
void ChildFile::ShutDown()
{
mProgramRunning = false;
if (mOutputThread.joinable())
{
mOutputThread.join();
}
mFS.close();
std::cout << "Shutdown Complete" << std::endl;
}
void ChildFile::Write(int aInt)
{
mFS<< "Inserting number: " << aInt << std::endl;
}
int main()
{
Parent *array[] = {new ChildFile("DriverOutput.txt"),new ChildFile("Output2.txt"), new ChildCout()};
for (int i = 0; i < 1000; i )
{
for (auto& child : array)
{
child->Push(i);
}
}
for (auto& child : array)
{
child->ShutDown();
}
return 0;
}
uj5u.com熱心網友回復:
您永遠不會通知執行緒關閉,并且謂詞甚至不考慮是否mProgramRunning已設定為 false。您需要在寫入后通知條件變數,mProgramRunning您需要相應地更改謂詞。
例如,這樣的東西應該可以作業,但我認為將Pop和WriteWithThread函式分開不是最理想的,因為簽名會迫使您將不必要的讀取添加到mProgramRunning.
...
std::list<int> Parent::Pop()
{
std::unique_lock<std::mutex> lock(mMutex);
//mCV.wait(lock, [&] {return !mList.empty(); });
bool running = true;
mCV.wait(lock, [this, &running] { // member variables accessed via this pointer -> capture this by value
running = running = mProgramRunning.load(std::memory_order_acquire);
return !running || !mList.empty(); // need to check for shutdown here too
});
std::list<int> removed;
if (running)
{
removed.splice(removed.begin(), mList, mList.begin());
}
return removed;
}
void Parent::WriteWithThread()
{
mThreadRunning = true;
while (mProgramRunning)
{
auto list = Pop();
if (list.empty())
{
break;
}
Write(list.front());
}
mThreadRunning = false;
}
...
void ChildCout::ShutDown()
{
mProgramRunning.store(false, std::memory_order_release);
mCV.notify_all(); // tell consumer about termination
if (mOutputThread.joinable())
{
mOutputThread.join();
}
std::cout << "Shutdown Complete" << std::endl;
}
...
void ChildFile::ShutDown()
{
mProgramRunning.store(false, std::memory_order_release);
mCV.notify_all(); // tell consumer about termination
if (mOutputThread.joinable())
{
mOutputThread.join();
}
mFS.close();
std::cout << "Shutdown Complete" << std::endl;
}
...
注意:我不是 100% 確定這完全符合您的預期,但它應該讓您知道出了什么問題。
關于std::move:這僅用于將不可分配給右值參考的東西轉換為可分配給右值參考的東西。
在
mOutputThread = std::move(std::thread(&Parent::WriteWithThread, this));
沒有必要使用std::move,因為std::thread(&Parent::WriteWithThread, this)它是一個可分配給右值參考的 xvalue,所以
mOutputThread = std::thread(&Parent::WriteWithThread, this);
足夠了。
std::move如果執行緒物件“有名字”,你需要 use :
std::thread tempThread(&Parent::WriteWithThread, this);
mOutputThread = std::move(tempThread);
此外在
mList.emplace_back(std::move(aInt));
兩者都沒有必要使用std::move,復制和移動分配int具有相同的效果。算術型別不會從使用std::move.
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/440069.html
上一篇:沒有物件切片的多型向量[C ]
