c/c++可以在单个线程中执行抢占式多任务吗?



C/c++中的抢占式 多任务:一个正在运行的线程可以被一些定时器中断并在任务之间切换吗?

许多使用绿色线程的虚拟机和其他语言运行时都是这样实现的;C/c++应用程序可以做同样的事情吗?

如果有,怎么做?

这将取决于平台,因此请根据特定平台对此的支持来讨论这一点;例如,如果你可以在Linux上的SIGALRM处理程序中做一些魔法来交换某种内部堆栈(也许使用longjmp ?),那将是伟大的!


我问是因为我很好奇。

我已经工作了好几年,使异步IO循环。当编写异步IO循环时,我必须非常小心,不要将昂贵的计算计算放入循环中,因为它会使循环DOS。

因此,我对异步IO循环可以恢复甚至完全支持某种绿色线程或此类方法的各种方式感兴趣。例如,在SIGALRM中对活动任务和循环迭代的次数进行采样,然后如果检测到任务阻塞,则将所有其他内容移动到新线程中,或者在此基础上进行一些巧妙的变化,以获得所需的结果。

最近在这方面有一些关于node.js的抱怨,在其他地方我也看到了关于其他运行时(如Go和Haskell)的诱人评论。但是,让我们不要离这个基本问题太远,即是否可以在C/c++中在单个线程中执行抢占式多任务

Windows的纤维是用户调度的执行单元,它们共享同一个线程。http://msdn.microsoft.com/en-us/library/windows/desktop/ms682661%28v=vs.85%29.aspx

UPD:关于用户调度上下文切换的更多信息可以在LuaJIT源代码中找到,它支持不同平台的协程,因此即使您根本不使用lua,查看源代码也会很有用。以下是摘要:http://coco.luajit.org/portability.html,

据我所知,你把通常不会混在一起的东西混在一起了:

  • Asynchrounous信号
    一个信号通常被传递到程序(因此在你的描述一个线程)在同一堆栈上,目前正在运行和运行注册的信号处理程序…在BSD unix中,有一个选项可以让处理程序在一个单独的所谓的"信号堆栈"上运行。

  • 线程和栈
    在自己的堆栈上运行线程的能力要求能够分配堆栈空间并保存和恢复状态信息(包括所有寄存器…)-否则在线程/进程之间进行干净的"上下文切换"是不可能的。通常这是在内核中实现的,并且经常使用某种形式的汇编程序,因为这是一个非常低级且对时间非常敏感的操作。

  • 调度器
    我敢说,每个能够运行线程的系统都有某种调度器…这基本上是一段以最高权限运行的代码。通常它已经订阅了一些硬件信号(时钟或其他),并确保没有其他代码直接(只是间接)注册到相同的信号。因此,调度器有能力抢占该系统上的任何东西。主要的关注点通常是在可用的内核上为线程提供足够的CPU周期来完成它们的工作。实现通常包括某种队列(通常不止一个)、优先级处理和其他一些东西。内核端线程的优先级通常高于其他线程。

  • 现代cpu

  • 在现代cpu上,实现是相当复杂的,因为涉及到处理几个核心,甚至一些"特殊线程"(即超线程)…由于现代cpu通常有几个级别的缓存等,因此适当地处理这些是非常重要的,以实现高性能。

所有这些都意味着你的线程可以并且很可能会被操作系统定期抢占。

在C中,你可以注册信号处理程序,然后在同一堆栈上抢占你的线程…注意,如果重新输入,信号处理程序是有问题的。您可以将处理放入信号处理程序中,也可以填充一些结构(例如队列),并让线程使用该队列内容…

关于setjmp/longjmp,您需要意识到,当与c++一起使用时,它们容易出现几个问题。

对于Linux,有一个可用的"完全抢占补丁",它允许你告诉调度程序以比内核线程(磁盘I/O…)更高的优先级运行你的线程!

部分参考文献见

  • http://www.kernel.org/doc/Documentation/scheduler/sched-rt-group.txt
  • http://www.kernel.org/doc/Documentation/scheduler/sched-design-CFS.txt
  • http://www.kernel.org/doc/Documentation/scheduler/
  • http://www.kernel.org/doc/Documentation/rt-mutex.txt
  • http://www.kernel.org/doc/Documentation/rt-mutex-design.txt
  • http://www.kernel.org/doc/Documentation/IRQ.txt
  • http://www.kernel.org/doc/Documentation/IRQ-affinity.txt
  • http://www.kernel.org/doc/Documentation/preempt-locking.txt
  • http://tldp.org/LDP/LG/issue89/vinayak2.html
  • http://lxr.linux.no/linux内核+ v3.1//sched.c # L3566
  • 我的线程可以帮助操作系统决定何时切换上下文吗?
  • https://www.osadl.org/Realtime-Preempt-Kernel.kernel-rt.0.html
  • http://www.rtlinuxfree.com/
  • 使用longjmp和setjmp是安全的吗?
  • 在C中使用setjmp和longjmp链接到c++库
  • http://www.cs.utah.edu/dept/old/texinfo/glibc手册- 0.02 -/- library_21.html

查看调度程序等的实际实现,请查看linux服务代码https://kernel.org。

由于你的问题不是很具体,我不确定这是否是一个真正的答案,但我怀疑它有足够的信息让你开始。

备注:

我不知道为什么你可能想要实现已经存在于操作系统的东西…如果它在一些异步I/O上有更高的性能,那么有几个选项通常在内核级别上具有最大的性能(即编写内核模式代码)…也许你可以澄清一下,以便给出一个更具体的答案。

用户空间线程库通常是协作的(例如:GNU pth, SGI的statethreads,…)。如果你想要抢占,你应该使用内核级线程。

您可能可以从SIGALARM信号处理程序中使用getcontext()/setcontext()...,但是如果可以工作,它最多将是混乱的。我看不出这种方法比内核线程或基于事件的I/O有什么优势:您获得了抢占性的所有非确定性,并且您的程序没有被分割成顺序的控制流。

正如其他人所概述的那样,先发制人可能不是很容易做到的。

通常的模式是使用协过程。

协过程是表达有限状态机(例如文本解析器,通信处理程序)的一种很好的方式。

你可以用一点预处理宏魔法来"模拟"协同过程的语法。


关于最优输入/输出调度

你可以看看Boost Asio: Proactor设计模式:无线程并发

Asio也有一个基于单个(IIRC)简单预处理器宏的协同过程"仿真"模型,结合一些巧妙设计的模板工具,使事情非常接近编译器对_stack-less co过程的支持。

示例HTTP Server 4是该技术的一个示例。

Boost Asio的作者(Kohlhoff)在他的博客上解释了这个机制和示例:无堆栈协程的简单指南

一定要看这个系列的其他文章!

你的问题毫无意义。你的一个线程会被中断吗?任何正在执行的代码都必须在线程中。每个线程基本上都是代码的顺序执行。一个线程要被打断,它必须被某些东西打断。您不能只是在现有线程中随机跳转作为对中断的响应。那么它就不再是通常意义上的线程了。

你通常是这样做的:

  • 你有多个线程,其中一个线程被挂起,直到警报被触发,
  • 或者,您有一个线程,它运行在某种事件循环中,在那里它从操作系统(以及其他来源)接收事件。当触发警报时,它会向线程的事件循环发送一条消息。如果你的线程正忙着做其他事情,它不会立即看到这个消息,但是一旦它回到事件循环并处理事件,它就会得到它,并做出反应。

标题是一个矛盾修饰法,一个线程是一个独立的执行路径,如果你有两个这样的路径,你有多个线程。

您可以使用setjmp/longjmp来执行一种"穷人"的多任务处理,但我不推荐这样做,因为它是合作的,而不是先发制人的。

C和c++本质上都不支持多线程,但是有许多库支持它,比如本地Win32线程、pthreads (POSIX线程)、boost线程,以及Qt和WxWidgets等框架也支持线程。

相关内容

  • 没有找到相关文章

最新更新