不清楚什么样的答案会让你满意.
C语言本身并没有提供任何这样的功能,因此任何解决方案都将依赖于提供该功能的其他东西.
请注意,自C.2011以来,C在<threads.h>
中引入了可选的线程支持,但没有线程属性操作可以让人们区分普通线程和轻量级线程执行.
可能是状态机,但需要操作系统的帮助.
如果您想知道如何仅使用C语言提供的原语来实现这一点,那么答案的 Select 几乎局限于实现您自己的状态机机制.但这需要您使用操作系统提供的机制,以便在访问通常会阻塞到完成的操作时启用非阻塞交互.这是为了让您有机会停驻当前执行上下文,并try 另一个执行上下文.
例如,在UNIX中,可以创建一个状态机数据 struct 来指示通过套接字发送文件的进度.
struct send_a_file_ctx {
int socket;
int file;
size_t file_size; /* total number of bytes to send */
size_t bytes_sent; /* bytes sent so far over the socket */
size_t buffer_size; /* number of file bytes in the buffer */
size_t buffer_offset; /* number of file bytes from buffer sent */
char buffer[]; /* buffered chunk of the file */
};
没有执行堆栈per se,但有一个 struct 跟踪缓冲区中字节传递到套接字的过程.
在UNIX中,send()
调用可以传入MSG_DONTWAIT
标志,以指示操作不应阻止完成,而应返回一个值,指示操作将被阻止.这将显示错误结果,errno
设置为EAGAIN
或EWOULDBLOCK
.然后,应将此上下文 struct 保存在某个队列中,以便将来再次提取以重试该操作.同一队列中可能有其他上下文 struct ,在此基础上可以再次try send()
,以查看是否可以取得进展.
UNIX提供了一些机制,允许您等待一个指示,表明您试图发送文件的一个或多个套接字将不再阻塞(请参阅poll
、select
、epoll
、kevent
,以及可能的其他变体,具体取决于您的操作系统),而不是在重试时不断循环.
What about ucontext.h
?
makecontext()
和swapcontext()
函数是classic UNIX系统为协作并发提供的机制.它允许进程使用备用执行堆栈切换其当前执行堆栈.因为它允许您切换执行堆栈,所以可以避免定义状态机 struct 的需要.状态是执行堆栈的固有状态.
然而,它并不能避免需要启用与通常会阻塞到完成的操作的非阻塞交互.在收到操作将被阻止的指示后,您将切换到备用堆栈,以重试之前被阻止的操作.
同样,为了避免连续的重试,您仍然需要实现某种堆栈的调度队列来恢复,并使用解复用等待原语(如poll
)来知道何时可以恢复哪个堆栈而不被阻塞.
虽然Linux
和BSD系统仍然支持ucontext.h
,但它不再是POSIX标准的一部分.它是原始POSIX的一部分.1-2001,但在POSIX中标记为过时.1-2004. 在POSIX中.2008年1月,已删除.
有什么好的 Select 吗?
这取决于你认为什么是"好的".
State machines?
如果您想要一些处理解复用的东西,并且只想编写代码来在恢复时运行状态机,那么可以考虑使用类似于libevent
或其分支/克隆之一的东西.它提供了一个通用接口,用于向解复用器注册上下文,并可在各种操作系统上运行.
然而,您仍然需要自己管理非阻塞交互.
Like threads?
如果您想要一个像使用线程一样易于编程的解决方案,并且避免管理自己的非阻塞上下文调度,那么您可以try GNU的pth
、Libwire、Windows Fibers甚至pthreads
.
GNU pth
可以提供类似线程的编程范例.如果您遵循提供的API,那么看起来您的所有线程都在执行块到完成的操作,而pth
实际上是在拦截调用,并在封面下执行非阻塞版本,并代表您切换上下文.
如果LGPL限制太多,您可以考虑使用Libwire,它是根据MIT许可证发布的.这是一个用户空间线程库,旨在为C带来一个类似GoLang的协同编程接口.
Windows Fibers是一种提供轻量级线程的工具,同时保持从块到完成的编程风格.Fibers有一个值得注意的特性,即fiber可以转换为thread,然后再转换回fiber.
POSIX pthreads
将contentionscope参数定义为pthread_attr_setscope
.此参数的一个值为PTHREAD_SCOPE_PROCESS
.这似乎是使用pthreads
实现轻量级调度的方法.但是,此范围的行为是由实现定义的.
工具书类