发布版本中的 C++ Future 错误值
C++ Future Wrong Value in Release Version
我写了一个小线程池class,并根据pschill(https://codereview.stackexchange.com/questions/229613/c-standard-thread-threadpool)给出的建议进行了调整。
我现在排队几个任务,直到一个线程可以处理它。
该任务由一个enum class State
、std::mutex
(状态)和一个std::packaged_task<R(Args...)>
组成。任务由调用实例创建(参见代码示例),并确保和测试生命周期。一旦线程准备就绪,任务就会被处理。
到目前为止,在每个构建配置中一切正常。
调试 x64 构建(最新的 VCpp)的执行给了我 4
的预期值。使用任何其他配置会导致 "random" 值....
我检查了涉及的每个对象的生命周期;果然不出所料。
任务执行
{
// May unnecessary but added due to previous errors
std::lock_guard<std::mutex>
m_state = State::INPROGESS;
}
m_task(args...);
{
std::lock_guard<std::mutex> lock(m_statelock);
m_state = State::DONE;
}
排队任务
template <typename R, typename ... Args>
void addTask(Task<R, Args...>& task, Args ... args) {
// m_tasks is a std::deque<std::function<void()>>
std::lock_guard<std::mutex> lock(m_tasklock);
m_tasks.emplace_back([&]() -> void {
task.execute(args...);
});
// Has own mutex
m_task_avaiable.notify_one(); // std::condition_variable
}
线程对象例程
[&]() -> void {
while (true) {
while (tpool.m_tasks.size() > 0) {
{
// Added due to crash with Release-config at asgining
// atomic<State> in Task
std::lock_guard<std::mutex> lock(statelock);
state = State::INPROGRESS;
}
std::function<void()> job;
{
std::lock_guard<std::mutex> lock(tpool.m_tasklock);
if (tpool.m_tasks.size() > 0) {
job = std::move(tpool.m_tasks[0]);
tpool.m_tasks.pop_front();
}
}
if (job) { job(); }
}
{
std::lock_guard<std::mutex> lock(statelock);
state = State::IDLE;
}
tpool.m_task_avaiable.wait(lock);
{
std::lock_guard<std::mutex> lock(statelock);
if (state == State::STOP) {
return; // To stop thread and make it joinable
}
}
}
}
主要
frm::util::ThreadPool pool(2);
frm::util::Task<int, int, int> t1([](int a, int b) {
std::this_thread::sleep_for(std::chrono::seconds(2));
std::cout << "Yey!...\n";
return a + b;
});
pool.addTask(t1, 2, 2);
// Direct access to member std::packaged_task<...> just for debugging
std::cout << t1.m_task.get_future().get() << '\n';
如果有帮助,任务对象 (x32) 的转储显示已请求未来(字节 61):
class std::mutex 48 Byte
1 0x02 0x00 0x00 0x00
5 0xbc 0x78 0x9f 0x0f
9 0x00 0x00 0x00 0x00
13 0x00 0x00 0x00 0x00
17 0x00 0x00 0x00 0x00
21 0x00 0x00 0x00 0x00
25 0x00 0x00 0x00 0x00
29 0x00 0x00 0x00 0x00
33 0x00 0x00 0x00 0x00
37 0x00 0x00 0x00 0x00
41 0xff 0xff 0xff 0xff
45 0x00 0x00 0x00 0x00
struct std::atomic<enum frm::util::Task<int,int,int>::State> 4 Byte
49 0x02 0x00 0x00 0x00
class std::packaged_task<int __cdecl(int,int)> 12 Byte
53 0x50 0xbd 0x15 0x01
57 0x00 0x00 0x00 0x00
61 0x01 0x00 0x00 0x00
VCpp19 中的 std::future<T>
、std::shared_future<T>
或 std::packaged_task<T>
有什么问题,还是我遗漏了什么?
我可以展示更多代码,但由于 SO 的规则和建议,我已经尽可能少地复制了。
也可能有帮助:
在使用 addTask(Task<R, Args...>& task, Args ... args)
addTask(Task<R, Args...>* task, Args ... args)
之前,如果添加了延迟,则会导致错误(不是在 x64 调试中),例如:
// Direct access to member std::packaged_task<...> just for debugging
std::this_thread::sleep_for(std::chrono::seconds(2));
std::cout << t1.m_task.get_future().get() << '\n';
您在以下代码中有未定义的行为:
m_tasks.emplace_back([&]() -> void {
task.execute(args...);
});
您的 lambda 通过 REFERENCE 捕获 args
。 emplace_back
将仿函数放入 m_tasks
,然后封闭范围结束,当任务被调用时,您将访问悬空引用。
你可以传值:
m_tasks.emplace_back([=]() -> void {
task.execute(args...);
});
我写了一个小线程池class,并根据pschill(https://codereview.stackexchange.com/questions/229613/c-standard-thread-threadpool)给出的建议进行了调整。
我现在排队几个任务,直到一个线程可以处理它。
该任务由一个enum class State
、std::mutex
(状态)和一个std::packaged_task<R(Args...)>
组成。任务由调用实例创建(参见代码示例),并确保和测试生命周期。一旦线程准备就绪,任务就会被处理。
到目前为止,在每个构建配置中一切正常。
调试 x64 构建(最新的 VCpp)的执行给了我 4
的预期值。使用任何其他配置会导致 "random" 值....
我检查了涉及的每个对象的生命周期;果然不出所料。
任务执行
{
// May unnecessary but added due to previous errors
std::lock_guard<std::mutex>
m_state = State::INPROGESS;
}
m_task(args...);
{
std::lock_guard<std::mutex> lock(m_statelock);
m_state = State::DONE;
}
排队任务
template <typename R, typename ... Args>
void addTask(Task<R, Args...>& task, Args ... args) {
// m_tasks is a std::deque<std::function<void()>>
std::lock_guard<std::mutex> lock(m_tasklock);
m_tasks.emplace_back([&]() -> void {
task.execute(args...);
});
// Has own mutex
m_task_avaiable.notify_one(); // std::condition_variable
}
线程对象例程
[&]() -> void {
while (true) {
while (tpool.m_tasks.size() > 0) {
{
// Added due to crash with Release-config at asgining
// atomic<State> in Task
std::lock_guard<std::mutex> lock(statelock);
state = State::INPROGRESS;
}
std::function<void()> job;
{
std::lock_guard<std::mutex> lock(tpool.m_tasklock);
if (tpool.m_tasks.size() > 0) {
job = std::move(tpool.m_tasks[0]);
tpool.m_tasks.pop_front();
}
}
if (job) { job(); }
}
{
std::lock_guard<std::mutex> lock(statelock);
state = State::IDLE;
}
tpool.m_task_avaiable.wait(lock);
{
std::lock_guard<std::mutex> lock(statelock);
if (state == State::STOP) {
return; // To stop thread and make it joinable
}
}
}
}
主要
frm::util::ThreadPool pool(2);
frm::util::Task<int, int, int> t1([](int a, int b) {
std::this_thread::sleep_for(std::chrono::seconds(2));
std::cout << "Yey!...\n";
return a + b;
});
pool.addTask(t1, 2, 2);
// Direct access to member std::packaged_task<...> just for debugging
std::cout << t1.m_task.get_future().get() << '\n';
如果有帮助,任务对象 (x32) 的转储显示已请求未来(字节 61):
class std::mutex 48 Byte
1 0x02 0x00 0x00 0x00
5 0xbc 0x78 0x9f 0x0f
9 0x00 0x00 0x00 0x00
13 0x00 0x00 0x00 0x00
17 0x00 0x00 0x00 0x00
21 0x00 0x00 0x00 0x00
25 0x00 0x00 0x00 0x00
29 0x00 0x00 0x00 0x00
33 0x00 0x00 0x00 0x00
37 0x00 0x00 0x00 0x00
41 0xff 0xff 0xff 0xff
45 0x00 0x00 0x00 0x00
struct std::atomic<enum frm::util::Task<int,int,int>::State> 4 Byte
49 0x02 0x00 0x00 0x00
class std::packaged_task<int __cdecl(int,int)> 12 Byte
53 0x50 0xbd 0x15 0x01
57 0x00 0x00 0x00 0x00
61 0x01 0x00 0x00 0x00
VCpp19 中的 std::future<T>
、std::shared_future<T>
或 std::packaged_task<T>
有什么问题,还是我遗漏了什么?
我可以展示更多代码,但由于 SO 的规则和建议,我已经尽可能少地复制了。
也可能有帮助:
在使用 addTask(Task<R, Args...>& task, Args ... args)
addTask(Task<R, Args...>* task, Args ... args)
之前,如果添加了延迟,则会导致错误(不是在 x64 调试中),例如:
// Direct access to member std::packaged_task<...> just for debugging
std::this_thread::sleep_for(std::chrono::seconds(2));
std::cout << t1.m_task.get_future().get() << '\n';
您在以下代码中有未定义的行为:
m_tasks.emplace_back([&]() -> void {
task.execute(args...);
});
您的 lambda 通过 REFERENCE 捕获 args
。 emplace_back
将仿函数放入 m_tasks
,然后封闭范围结束,当任务被调用时,您将访问悬空引用。
你可以传值:
m_tasks.emplace_back([=]() -> void {
task.execute(args...);
});