【问题标题】:C: How to make threads communicate with each other?C:如何让线程相互通信?
【发布时间】:2014-09-16 19:21:59
【问题描述】:

有趣的是,这似乎是一个基本问题,但我找不到 C 语言的任何示例(在 SO 中,我只找到了 Python、C# 和 C++)。

重点是:作为一个Qt程序员,当我需要让一些数据在不同线程之间传输时,我会在它们之间建立一个信号槽连接,并使用emit signal机制来完成这项工作。

但现在我正在为嵌入式 Linux 开发一个 C 应用程序,我需要在其中做类似的工作,但我没有可用的 Qt 机制。问题是:如何让两个或多个线程在 C 中以类似于 Qt 的信号和槽的方式相互通信?

我知道共享数据的一种方法是使用全局变量,其更改受互斥锁保护。但即便如此,我也可能无法以异步方式执行系统:我必须有一个循环来不断检查变量是否已更改。但是如果我想在另一个线程完成一些工作之后执行一个线程的特定方法(所以,以异步方式)?那么这种方式似乎失败了。

注意:虽然我使用的是嵌入式 Linux,因此,提及一些采用 POSIX 功能的选项和其他“与 Linux 相关的方式”会有所帮助,但如果给社区更多的时间,它仍然会更好不严格基于某个特定平台的解决方案(如果可能的话)。

【问题讨论】:

  • 您的嵌入式系统的规格是什么?也许你可以使用 C++?
  • @MaximYegorushkin 实际上我想在这个项目中使用 Qt(所以 C++)(完全可行),所以我可以使用信号和插槽等,但团队的其他成员不赞成并更愿意继续使用纯C =T
  • 我能感觉到你的痛苦:(

标签: c linux multithreading pthreads


【解决方案1】:

阅读好tutorial on pthreads。您想了解更多关于condition variablesmutexes 一起使用的信息。

条件变量和互斥锁应该足以满足您的需求。

您还可以在线程之间使用最传统的inter-process communication 机制,例如一个pipe(7)(可能与poll(2)...)。所以阅读Advanced Linux Programming并研究syscalls(2)pthreads(7)

避免在线程之间使用signal(7)-s 并注意signal-safety(7)。但是请参阅signalfd(2)eventfd(2)userfaultfd(2)(您可能会使用它cleverly handle SIGSEGV)并从Calling Qt functions from Unix signal handler 建议的方法中获得灵感。

观察正在运行的多线程 Linux processstrace(1)ltrace(1)gdb(1)。你会明白有几个pthreads(7) 原语正在使用futex(7)

GNU glibcmusl-libc 都是 open source 并实现 pthreads 规范(并且在它们之上构建了 GlibGTKQtPOCO)。 我邀请您研究他们的源代码。

【讨论】:

  • 不知道syscalls 是如何适应这个的,否则+1。
  • 他们总结了许多与 IPC 相关的东西(可以在线程之间使用):pipesocketeventfd 等等等等等等……我不想列出所有syscalls 与 IPC 相关!
  • 是的,但恕我直言,这对高级开发人员来说最有趣。我认为对于初学者来说,让我们专注于基础知识(如互斥锁、信号量以及如何将它们与pthreads 一起使用;它们如何用于协调对共享数据的访问等)。
  • 20 年来,我从未见过有人使用甚至谈论使用信号进行线程间通信?!
  • 关于 SO 的一些问题提到了这一点,但我不建议使用信号进行线程间通信。
【解决方案2】:

一种方法是通过异步队列在线程之间使用消息传递。这样可以避免在线程之间使用共享数据,并且只有队列需要是线程安全的。

可以使用不同的同步原语来实现异步队列:

  • 管道或插座。
  • 受互斥体和条件变量保护的队列。
  • 非阻塞或无锁队列。

【讨论】:

  • Python 对此有抽象层。
  • @PavelŠimerda 如果只有 Python 没有臭名昭著的 GIL...
  • 啊,是的,进程间队列在 Python 中更有用一点。我也看到这个问题明确排除了 Python,我最初没有看到它。
【解决方案3】:

您想要通知诸如“数据可用”之类的事件的线程可以注册一个可由通知线程触发的回调函数。您可以为此使用函数指针。

例如:线程 2 为一个或多个事件注册一个回调函数。线程 1 在条件或事件发生时调用注册的函数。

【讨论】:

  • @Basile:原始问题是“如何在不轮询全局变量的情况下以异步方式与另一个线程共享数据”。因此,为了实现这一点,您可以使用回调函数,这是许多库中的通用做法。
  • 请用什么注册一个回调函数?哪个 API?
  • 您必须创建自己的 API 才能做到这一点。它只是一个函数指针。
  • 等等,它是一个API,还是只是一个函数指针?这个线程如何安全?
【解决方案4】:

生产者和消费者线程应该捕获彼此的 tid。生产者可以发送: pthread_kill(消费者ID,SIGUSR1); 消费者使用 SIGUSR1 的信号处理程序设置,并且可以从 pthread_setspecific() 保存的公共 std::queue 中检索生成的结果。 生产者和消费者可以继续他们的任务,而不会被信号量或 cond var/mutex 锁定。

【讨论】:

    猜你喜欢
    • 2011-10-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-03
    • 2021-11-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多