没有SendMessage的C++中的跨线程操作
Cross-thread operations in C++ without SendMessage
我正在寻找一种以 SendMessage 允许的方式执行跨线程操作的方法。换句话说,如何让一个线程在另一个线程中执行一些功能。但是我想在没有 SendMessage 的情况下这样做,因为这需要 window 并不总是可用。
同步异步都可以。
.NET 用 System.Windows.Threading.Dispatcher class 做到了,所以肯定有办法吗?
所以我猜我们在这里谈论 Windows OS,对吧?你应该在你的问题中指定。例如,Windows 的解决方案可能与 Linux 的解决方案不同。
现在,关于您的问题,针对该情况的任何解决方案都会迫使您的线程等待某个事件发生(将任务排入队列),或者无休止地轮询任务。
所以我们讨论的是某种互斥体、条件变量或某种特殊的休眠函数。
一种(简单)且不可移植的方式或将 "tasks" 发送到其他线程是使用内置的 Win32 - APC
机制(A同步P程序Call).
它利用了函数 QueueUserAPC and SleepEx,我已经在我的 Windows 10 + Visual studio 2015
上对这个解决方案进行了迷你测试
namespace details {
void waitforTask() noexcept {
SleepEx(INFINITE, TRUE);
}
void __stdcall executeTask(ULONG_PTR ptr) {
if (ptr == 0) {
return;
}
std::unique_ptr<std::function<void()>> scopedPtr(reinterpret_cast<std::function<void()>*>(ptr));
(*scopedPtr)();
}
}
template<class F, class ... Args>
void sendTask(void* threadHandle, F&& f, Args&& ... args) {
auto task =
std::make_unique<std::function<void()>>(std::bind(std::forward<F>(f), std::forward<Args>(args)...));
const auto res = QueueUserAPC(&details::executeTask,
threadHandle,
reinterpret_cast<ULONG_PTR>(task.get()));
if (res == 0) {
throw std::runtime_error("sendTask failed.");
}
task.release();
}
使用示例:
std::thread thread([] {
for (;;) {
details::waitforTask();
}
});
sendTask(thread.native_handle(), [](auto literal) {
std::cout << literal;
}, "hello world");
此解决方案还展示了如何使用 Win32 而不会实际污染用 C++ 代码编写的与 win32 代码无关的业务逻辑。
这个解决方案也可以适用于跨平台解决方案,而不是使用内部的、半文档化的 win32 任务队列,可以使用 std::queue
和 std::function<void()>
构建自己的任务队列。可以使用 std::condition_variable
代替睡眠状态。这是任何线程池为了获取和执行任务而在幕后所做的事情。如果您确实想要跨平台解决方案,我建议使用谷歌搜索 "C++ thread pool" 以查看此类任务队列的示例。
我正在寻找一种以 SendMessage 允许的方式执行跨线程操作的方法。换句话说,如何让一个线程在另一个线程中执行一些功能。但是我想在没有 SendMessage 的情况下这样做,因为这需要 window 并不总是可用。
同步异步都可以。
.NET 用 System.Windows.Threading.Dispatcher class 做到了,所以肯定有办法吗?
所以我猜我们在这里谈论 Windows OS,对吧?你应该在你的问题中指定。例如,Windows 的解决方案可能与 Linux 的解决方案不同。
现在,关于您的问题,针对该情况的任何解决方案都会迫使您的线程等待某个事件发生(将任务排入队列),或者无休止地轮询任务。
所以我们讨论的是某种互斥体、条件变量或某种特殊的休眠函数。
一种(简单)且不可移植的方式或将 "tasks" 发送到其他线程是使用内置的 Win32 - APC
机制(A同步P程序Call).
它利用了函数 QueueUserAPC and SleepEx,我已经在我的 Windows 10 + Visual studio 2015
上对这个解决方案进行了迷你测试namespace details {
void waitforTask() noexcept {
SleepEx(INFINITE, TRUE);
}
void __stdcall executeTask(ULONG_PTR ptr) {
if (ptr == 0) {
return;
}
std::unique_ptr<std::function<void()>> scopedPtr(reinterpret_cast<std::function<void()>*>(ptr));
(*scopedPtr)();
}
}
template<class F, class ... Args>
void sendTask(void* threadHandle, F&& f, Args&& ... args) {
auto task =
std::make_unique<std::function<void()>>(std::bind(std::forward<F>(f), std::forward<Args>(args)...));
const auto res = QueueUserAPC(&details::executeTask,
threadHandle,
reinterpret_cast<ULONG_PTR>(task.get()));
if (res == 0) {
throw std::runtime_error("sendTask failed.");
}
task.release();
}
使用示例:
std::thread thread([] {
for (;;) {
details::waitforTask();
}
});
sendTask(thread.native_handle(), [](auto literal) {
std::cout << literal;
}, "hello world");
此解决方案还展示了如何使用 Win32 而不会实际污染用 C++ 代码编写的与 win32 代码无关的业务逻辑。
这个解决方案也可以适用于跨平台解决方案,而不是使用内部的、半文档化的 win32 任务队列,可以使用 std::queue
和 std::function<void()>
构建自己的任务队列。可以使用 std::condition_variable
代替睡眠状态。这是任何线程池为了获取和执行任务而在幕后所做的事情。如果您确实想要跨平台解决方案,我建议使用谷歌搜索 "C++ thread pool" 以查看此类任务队列的示例。