C ++分离线程错误
C++ detached thread bug
在花了一天时间解决一个神秘的错误后,我寻求你的帮助。
当我运行下面的代码时,你明白为什么"Hello 3"的输出是重复的吗?
#include <iostream>
#include <future>
#include <string>
#include <functional>
#include <type_traits>
#include <unistd.h>
template <class Fn, class... Args>
inline decltype(auto) runTerminateOnException(Fn&& fn, Args&&... args) {
try {
return std::invoke(std::forward<Fn>(fn), std::forward<Args>(args)...);
} catch (...) {
std::terminate();
}
}
struct RunTerminateOnExceptionInvoker {
template <class Fn, class... Args>
decltype(auto) operator()(Fn&& fn, Args&&... args) const {
return runTerminateOnException(std::forward<Fn>(fn), std::forward<Args>(args)...);
}
};
template <class Fn, class... Args>
inline decltype(auto) runAsyncTerminateOnException(Fn&& fn, Args&&... args) {
usleep(1000);
return std::async(std::launch::async, RunTerminateOnExceptionInvoker(), std::forward<Fn>(fn), std::forward<Args>(args)...);
}
template <class Fn, class... Args>
inline void runOnDetachedThreadTerminateOnException(Fn&& fn, Args&&... args) {
usleep(1000000);
std::thread(RunTerminateOnExceptionInvoker(), std::forward<Fn>(fn), std::forward<Args>(args)...).detach();
}
struct A {
template <class T>
static void g(double x, const std::shared_ptr<std::string> &s) {
T t{};
std::cout << "g() : x = " << x << ", *s = " << *s << ", t = " << t << std::endl;
}
static void f(double x, std::shared_ptr<std::string> &s1, std::shared_ptr<std::string> &s2, std::shared_ptr<std::string> &s3) {
printf("Coucou 1\n");
runAsyncTerminateOnException(g<double>, x, s1); // Working
printf("Coucou 2\n");
auto future1 = runAsyncTerminateOnException(g<double>, x, s2); // Working
printf("Coucou 3\n");
runOnDetachedThreadTerminateOnException(g<double>, x, s3); // Working
}
};
int main() {
auto s1 = std::make_shared<std::string>("Hello 1");
auto s2 = std::make_shared<std::string>("Hello 2");
auto s3 = std::make_shared<std::string>("Hello 3");
A::f(10., s1, s2, s3);
printf("Coucou 4\n");
return 0;
}
输出:
Coucou 1
g() : x = 10, *s = Hello 1, t = 0
Coucou 2
Coucou 3
g() : x = 10, *s = Hello 2, t = 0
Coucou 4
g() : x = 10, *s = Hello 3, t = 0
g() : x = 10, *s = Hello 3, t = 0
谢谢
你在这里有未定义的行为。由于线程已分离,因此您不能保证 *s3
会与分离的线程一样长。 main
在分离的工作线程完成执行之前结束,所有这些字符串都超出范围。
您必须按值传递共享指针才能真正获得它们的引用计数能力。目前,对于每个测试,您只是通过引用传递一个 shared_ptr
实例,这并不是特别有用。
如果按值将它们传递给 A::f
、things work as expected。
我无法 完全 弄清楚编译器在你的情况下做了什么魔法,并对这个具体结果感到有些惊讶,但幸运的是我可以说 "it's UB" 然后把东西留在那里 ;)
坦率地说,我什至不确定让分离的线程 运行 超过 main
的末尾是否有效,但我会让其他人解决这个问题。
在花了一天时间解决一个神秘的错误后,我寻求你的帮助。
当我运行下面的代码时,你明白为什么"Hello 3"的输出是重复的吗?
#include <iostream>
#include <future>
#include <string>
#include <functional>
#include <type_traits>
#include <unistd.h>
template <class Fn, class... Args>
inline decltype(auto) runTerminateOnException(Fn&& fn, Args&&... args) {
try {
return std::invoke(std::forward<Fn>(fn), std::forward<Args>(args)...);
} catch (...) {
std::terminate();
}
}
struct RunTerminateOnExceptionInvoker {
template <class Fn, class... Args>
decltype(auto) operator()(Fn&& fn, Args&&... args) const {
return runTerminateOnException(std::forward<Fn>(fn), std::forward<Args>(args)...);
}
};
template <class Fn, class... Args>
inline decltype(auto) runAsyncTerminateOnException(Fn&& fn, Args&&... args) {
usleep(1000);
return std::async(std::launch::async, RunTerminateOnExceptionInvoker(), std::forward<Fn>(fn), std::forward<Args>(args)...);
}
template <class Fn, class... Args>
inline void runOnDetachedThreadTerminateOnException(Fn&& fn, Args&&... args) {
usleep(1000000);
std::thread(RunTerminateOnExceptionInvoker(), std::forward<Fn>(fn), std::forward<Args>(args)...).detach();
}
struct A {
template <class T>
static void g(double x, const std::shared_ptr<std::string> &s) {
T t{};
std::cout << "g() : x = " << x << ", *s = " << *s << ", t = " << t << std::endl;
}
static void f(double x, std::shared_ptr<std::string> &s1, std::shared_ptr<std::string> &s2, std::shared_ptr<std::string> &s3) {
printf("Coucou 1\n");
runAsyncTerminateOnException(g<double>, x, s1); // Working
printf("Coucou 2\n");
auto future1 = runAsyncTerminateOnException(g<double>, x, s2); // Working
printf("Coucou 3\n");
runOnDetachedThreadTerminateOnException(g<double>, x, s3); // Working
}
};
int main() {
auto s1 = std::make_shared<std::string>("Hello 1");
auto s2 = std::make_shared<std::string>("Hello 2");
auto s3 = std::make_shared<std::string>("Hello 3");
A::f(10., s1, s2, s3);
printf("Coucou 4\n");
return 0;
}
输出:
Coucou 1
g() : x = 10, *s = Hello 1, t = 0
Coucou 2
Coucou 3
g() : x = 10, *s = Hello 2, t = 0
Coucou 4
g() : x = 10, *s = Hello 3, t = 0
g() : x = 10, *s = Hello 3, t = 0
谢谢
你在这里有未定义的行为。由于线程已分离,因此您不能保证 *s3
会与分离的线程一样长。 main
在分离的工作线程完成执行之前结束,所有这些字符串都超出范围。
您必须按值传递共享指针才能真正获得它们的引用计数能力。目前,对于每个测试,您只是通过引用传递一个 shared_ptr
实例,这并不是特别有用。
如果按值将它们传递给 A::f
、things work as expected。
我无法 完全 弄清楚编译器在你的情况下做了什么魔法,并对这个具体结果感到有些惊讶,但幸运的是我可以说 "it's UB" 然后把东西留在那里 ;)
坦率地说,我什至不确定让分离的线程 运行 超过 main
的末尾是否有效,但我会让其他人解决这个问题。