我正在尝试学习一些正确的线程管理,但我对这个人为的示例遇到了一些问题。这个想法是“Holder”类可以启动一个作业,它是“Wait”类的一个实例,它触发一个线程做一些事情(等待一段时间),然后调用“Holder”中的回调来做一些事情一些清理或其他什么。 我将“等待”实例放入 std::vector 中,当回调告诉我它已完成时。我将其从列表中删除。
更新:更正注释掉的行
//waitlist.erase(it); // 转储等待对象
如果我调用上面的行来指示作业已完成并将其从“列表”中删除,则会导致主线程过早退出,为什么?正确的去除方法是什么?
#include <chrono>
#include <ctime>
#include <functional>
#include <iostream>
#include <thread>
#include <vector>
using Time = std::chrono::system_clock;
using Seconds = std::chrono::seconds;
using Timepoint = Time::time_point;
class Wait {
private:
Timepoint target;
std::thread thread;
public:
Wait(unsigned int waitFor, unsigned short id, std::function<void(unsigned int)> callback_)
{
std::cout << "new Wait objet needs to wait for: " << waitFor << " seconds" << std::endl;
target = Time::now() + Seconds(waitFor);
// Create a thread to notify us when complete
thread = std::thread([callback_, waitFor, id]() {
std::this_thread::sleep_for(Seconds(waitFor));
std::cout << "from thread: " << std::this_thread::get_id() << std::endl;
callback_(id);
});
}
bool isDone()
{
Timepoint now = Time::now();
std::chrono::duration<float> difference = now - target;
return (difference.count() > 0); // Thanks @Jesper
}
};
class Holder {
private:
std::vector<Wait> waitlist;
public:
unsigned short addTask(unsigned int waitFor)
{
unsigned int sz = waitlist.size();
waitlist.push_back(Wait(waitFor, sz, std::bind(&Holder::callback, this, std::placeholders::_1)));
return waitlist.size() - 1;
}
bool isDone(unsigned int n)
{
if (waitlist.size() >= n) {
return waitlist.at(n).isDone();
}
else return false; // failed
}
void callback(unsigned int id)
{
std::cout << "All done with Wait object" << std::endl;
auto it = waitlist.begin() + id;
/* comment/uncomment next line*/
//waitlist.erase(it); // dump the wait object
}
};
int main()
{
Holder h;
// Create a task which spawns a thread, which notifies us when complete
unsigned int id1 = h.addTask(3);
// Just to show that while main thread is running, the completion status
// of thread changes
for (auto c = 0; c < 5; c++)
{
std::this_thread::sleep_for(Seconds(1));
std::cout << "from main: " << std::this_thread::get_id() << " - " << h.isDone(id1) << std::endl;
}
// Just to show the main thread continues on. It doesn't when line mentioned
// earlier is uncommented
while(1) {
std::this_thread::sleep_for(Seconds(1));
std::cout << "Doing other stuff.." << std::endl;
}
return 0;
}
预期输出:
new Wait objet needs to wait for: 3 seconds
from main: 140234566031168 - 0
from main: 140234566031168 - 0
from thread: 140234566026816
All done with Wait object
from main: 140234566031168 - 1
from main: 140234566031168 - 1
from main: 140234566031168 - 1
Doing other stuff..
Doing other stuff..
Doing other stuff..
Doing other stuff..
Doing other stuff..
任何关于我忽略的(我确定基本)前提的见解/指导将不胜感激。
保罗
好的, 我明白我做错了什么。回调(从 Wait 对象中实例化的线程执行)试图删除容纳该线程的 Wait 对象,该对象调用线程的析构函数并导致主线程存在。
使用 std::async 可以解决连接/分离问题(我猜类似于上面提到的 C++20 的 std::jthread 选项),但您可以简单地检查它是否已完成,例如;
if (async_task.wait_for(std::chrono::seconds(0)) == std::future_status::ready) // async done
一旦我有办法检查它是否完成,我可以简单地检查是否完成,然后从“Holder”类中的向量中删除该任务。
我不能对线程做同样的事情,因为如果我使用 join(),我必须等待它,并且我不想要阻塞调用,如果我使用 detach(),它将把它与main 实例,我不知道它何时/是否完成。如果实例化的线程在没有看到其中任何一个的情况下终止,它会导致主线程终止,这就是为什么我在线程终止后没有进入“做其他事情...”部分。