我一直遵循这样的概念,即多线程只能在多处理器系统上实现,在该系统上,每个线程分配一个以上的处理器,并且每个线程可以同时执行。在这种情况下,没有调度,因为每个线程都有各自专用的资源。但是我经常在一个可以在单处理器系统上执行多线程的地方阅读它。这是对的吗?如果是,那么单处理器和多处理器系统之间有什么区别?
我一直遵循这样的概念,即多线程只能在多处理器系统上实现,在该系统上,每个线程分配一个以上的处理器,并且每个线程可以同时执行。在这种情况下,没有调度,因为每个线程都有各自专用的资源。但是我经常在一个可以在单处理器系统上执行多线程的地方阅读它。这是对的吗?如果是,那么单处理器和多处理器系统之间有什么区别?
Answers:
当然,它可以在单处理器系统上完成,实际上,这种方法要容易得多。它的运行方式与运行多个进程相同-内核通过计时器中断或其他类似机制挂起一个进程,保存其计算机状态,然后用先前保存的另一个状态替换该状态-唯一的区别是两个同一进程的线程共享相同的虚拟内存空间,从而使任务切换更加高效。
实际上,在多处理器系统上进行多线程处理要困难得多,因为存在从多个cpus /内核同时访问内存的问题,以及由此引起的所有讨厌的内存同步问题。
我在某个可以在单处理器系统上执行多线程的地方也曾阅读过它。这是对的吗?如果是,那么单处理器和多处理器系统之间有什么区别?
是的,您可以在单个处理器系统上执行多线程。
在多处理器系统中,多个线程在不同的内核上同时执行。例如-如果有两个线程和两个核心,则每个线程将在单个核心上运行。
在单处理器系统中,多个线程执行一个接一个的执行,或者等待一个线程完成或被操作系统抢占,这取决于线程的优先级和操作系统策略。 ,相对于用户空间应用程序所需的应用程序响应时间。
时间比较(示例):
如果两个线程各自花费10us执行,那么在2处理器系统上,净时间为10us
如果两个线程每个执行花费10us,则在1个处理器系统上,净时间为20us
是的,您完全可以。以前(Win 95?),我们从协作多任务转向多线程,因为有人总是搞砸协作部分。您计算机上的每个程序都有至少一个线程。可能更多。CPU每秒仅在所有这些线程之间疯狂切换数百万次。如果它们都不起作用,它甚至可能会闲置一段时间。
多核系统仅意味着其中两个或多个线程可以并行运行。
但是,这样做可以减少很多麻烦。在单核计算机上使用多线程所做的全部工作就是模拟多任务处理。
Mulitasking足以防止由于长时间运行而导致GUI线程锁定。但是,除非您从编译器或Langauge获得一些帮助(例如C#async ... await),否则通常实现起来很复杂。结果,许多GUI程序员仅使用多线程和调用伪造多任务。如果该代码在单核或多核上运行,则不适合这样做。
最重要的是,多任务处理不适用于CPU限制的操作。但是,所有异步问题中有95%不受CPU限制。它们是网络或磁盘绑定。在单核计算机上,多线程也无法解决CPU受限的问题。如果有两个线程都需要100%的CPU时间(相同的编程或不同的线程),但只有一个内核可以运行它们,则CPU只需在两个线程都以49%的速度运行之间切换,而其余的2%则用于所有这些其他只做一点点的线程。
最后,实际上只有很少的问题是多线程的。只需尝试对Fibonacci序列(每对一个线程)进行多线程处理,而不必使其速度变慢,对内存的要求更高并且更加复杂。
tl; dr; 您需要多线程处理和多核计算机才能解决CPU受限的问题。大多数异步问题不受CPU限制。多任务处理就足够了。而且,即使在单核计算机上,您也可以使用线程完全执行多任务。
这是一个非常简化的示例。它实际上是我正在构建的程序的原型。它是在单线程中实现协作式多任务处理的一种实现。
main
只需将quit
标志设置为false,然后填充功能指针数组(任务),然后调用loop
。
loop
用于setjmp
设置非本地跳转的返回点(从函数中跳转回执行中的先前位置),然后继续调用第一个任务(函数)。
每个任务都以结尾yield()
。也就是说,实际上没有任何任务起作用return
。它们不仅不包含一条return;
语句(这很好,因为它们是void
函数,即过程),而且return
即使到达那里也不会到达,因为yield
跳转回setjmp
调用,这时该if
语句的值为1在中loop
。由该语句控制的if
语句在重新进入while
循环之前选择其他任务。
因此,每个任务功能都会运行多次,从而产生调度程序(该if(setjmp...
语句),该调度程序选择要运行的新任务。
#include <stdio.h>
#include <setjmp.h>
jmp_buf dispatch;
int ntasks;
void (*task[10])(void);
int quit;
void yield(void) {
longjmp(dispatch, 1);
}
void loop() {
static int i = 0;
if(setjmp(dispatch))
i = (i+1) % ntasks;
while(!quit)
task[i]();
}
int acc = 0;
void a(void) {
if (acc > 10) quit = 1;
printf("A\n");
yield();
}
void b(void) {
acc *= 2;
printf("B\n");
yield();
}
void c(void) {
acc += 1;
printf("C\n");
yield();
}
int main() {
quit = 0;
ntasks = 3;
task[0] = a;
task[1] = b;
task[2] = c;
loop();
return 0;
}
此示例与单处理器多任务计算机系统之间的区别在于,真正的处理器支持在执行过程中中断任务,并在以后从同一位置恢复该任务。在将任务作为单个功能的C模拟中,这实际上是不可能的。但是,这些任务可以由一系列C函数组成,每个C函数都产生给分派器(一个函数指针数组,或者一个链表)。
yield()
,因此每个线程都必须在调用yield之前完成。因此,无法一次拥有多个活动线程,也无法在它们之间进行切换。因此,只需让任务返回(而不是调用yield)而根本不使用setjmp
/ longjmp
,就可以使事情变得更加简单。