【问题标题】:Cross-thread operations in C++ without SendMessage没有 SendMessage 的 C++ 中的跨线程操作
【发布时间】:2016-12-25 10:00:53
【问题描述】:

我正在寻找一种以 SendMessage 允许的方式执行跨线程操作的方法。换句话说,如何让一个线程在另一个线程中执行某些功能。但我想在没有 SendMessage 的情况下这样做,因为这需要一个并不总是可用的窗口。

同步或异步都可以。

.NET 使用 System.Windows.Threading.Dispatcher 类来实现,所以肯定有办法吗?

【问题讨论】:

  • 您需要一个工作队列,目标线程在处理其项目时被卡住,这正是 .NET 中 Dispatcher 类提供的。
  • 我需要使用哪些功能?我正在查看“线程池 API”文档,但它似乎只能在特殊工作线程上排队操作。如果工作线程需要在主 UI 线程上排队执行操作怎么办?
  • 对于不习惯原生 Windows 开发的人来说,这似乎很奇怪,但是有一个不可见的窗口是非常普遍的。在许多情况下,即使线程从不显示单个用户可见元素,它也需要一个线程,这是完全可以接受的。因此,如果您必须交换或收听消息,只需创建一个窗口即可。虽然解释您的实际问题可能是一个好主意 - 您很少需要在特定线程上执行某些操作。
  • 如果你有一个UI线程使用SendMessage,只需创建一个仅消息窗口并用于跨线程业务。
  • @MatteoItalia 好的,你从哪里得知我想劫持另一个线程?我问的正是这些函数的作用,我不知道 QueueUserAPC 似乎是最好的选择。问题解决了。

标签: c++ multithreading sendmessage


【解决方案1】:

所以我猜我们在这里谈论的是 Windows 操作系统,对吧?你应该在你的问题中说明这一点。例如,适用于 Windows 的解决方案可能与适用于 Linux 的解决方案不同。

现在,关于您的问题,针对这种情况的任何解决方案都将迫使您的线程要么等待某个事件发生(将任务排队),要么无休止地轮询任务。

所以我们讨论的是某种互斥体、条件变量或一些特殊的睡眠函数。

一种(简单)且不可移植的方式或向其他线程发送“任务”是使用内置的 Win32 - APC 机制(A同步Procedure C全部)。

它利用函数QueueUserAPC 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::queuestd::function&lt;void()&gt; 构建自己的任务队列。可以使用std::condition_variable 而不是在警报状态下睡觉。这是任何线程池在幕后所做的,以便获取和执行任务。如果您确实想要一个跨平台的解决方案,我建议您搜索“C++ 线程池”以查看此类任务队列的示例。

【讨论】:

  • 谢谢,QueueUserAPC 正是我所需要的。你知道是否有同步的等价物吗?
  • 在此上下文中定义同步。
  • 那么,基本上你希望线程 A 将任务排入线程 B 并等待线程 B 完成任务?我不明白这个问题。要求另一个线程做某事而你做其他事情(一)异步的定义
  • 以 SendMessage 的方式为例 - 它等待窗口过程处理消息然后返回。这也是 Dispatcher.Invoke 和 Dispatcher.BeginInvoke 方法的区别。
  • @user1610015 这是一件非常糟糕的事情。如果您必须使线程卡住,请将 win32 事件添加到您的函数中,并在函数结束时让函数设置该事件。然后在排队线程中等待该事件。同样,这错过了异步代码的全部意义。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-03-27
  • 1970-01-01
  • 2011-01-15
  • 1970-01-01
  • 2020-08-08
  • 2015-02-17
相关资源
最近更新 更多