原文:https://github.com/angrave/SystemProgramming/wiki/Pthreads%2C-Part-1%3A-Introduction
线程是“执行线程”的缩写。它表示 CPU 具有(并将执行)的指令序列。要记住如何从函数调用返回,并存储自动变量和参数的值,线程使用栈。
好吧,对于所有意图和目的,线程是一个过程(意味着创建一个线程类似于fork
),除了没有复制意味着没有写入副本。这允许进程共享相同的地址空间,变量,堆,文件描述符等。
创建线程的实际系统调用类似于fork
;这是clone
。我们不会详细说明,但您可以阅读手册页,请记住它不在本课程的直接范围内。
LWP 或线程更倾向于分配许多场景,因为创建它们的开销要少得多。但在某些情况下(特别是 python 使用它)多处理是使代码更快的方法。
您的主要功能(以及您可能调用的其他功能)具有自动变量。我们将使用栈将它们存储在内存中,并使用简单的指针(“栈指针”)跟踪栈的大小。如果线程调用另一个函数,我们将栈指针向下移动,这样我们就有更多的空间用于参数和自动变量。一旦从函数返回,我们就可以将栈指针移回其先前的值。我们保留旧栈指针值的副本 - 在栈上!这就是为什么从函数返回非常快 - 很容易“释放”自动变量使用的内存 - 我们只需要更改栈指针。
在多线程程序中,有多个栈但只有一个地址空间。 pthread 库分配一些栈空间(在堆中或使用主程序栈的一部分)并使用clone
函数调用在该栈地址处启动线程。总地址空间可能看起来像这样。
您可以在进程内运行多个线程。你免费得到第一个帖子!它运行你在'main'中编写的代码。如果需要更多线程,可以使用 pthread 库调用pthread_create
创建新线程。您需要将指针传递给函数,以便线程知道从哪里开始。
您创建的线程都存在于同一个虚拟内存中,因为它们是同一进程的一部分。因此,他们都可以看到堆,全局变量和程序代码等。因此,您可以在同一个进程内同时处理两个(或多个)CPU。由操作系统决定将线程分配给 CPU。如果你有比 CPU 更多的活动线程,那么内核会将线程分配给 CPU 很短的时间(或直到它用完了要做的事情),然后自动切换 CPU 在另一个线程上工作。例如,一个 CPU 可能正在处理游戏 AI 而另一个线程正在计算图形输出。
要使用 pthread,您需要包含pthread.h
并且需要使用-pthread
(或-lpthread
)编译器选项进行编译。此选项告诉编译器您的程序需要线程支持
要创建线程,请使用函数pthread_create
。这个函数有四个参数:
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
- 第一个是指向一个变量的指针,该变量将保存新创建的线程的 id。
- 第二个是指向属性的指针,我们可以使用它来调整和调整 pthreads 的一些高级功能。
- 第三个是指向我们想要运行的函数的指针
- 第四个是指向我们的函数的指针
论点void *(*start_routine) (void *)
很难读懂!它表示一个指针,它接受一个void *
指针并返回一个void *
指针。它看起来像一个函数声明,除了函数的名称用(* .... )
包装
这是最简单的例子:
#include <stdio.h>
#include <pthread.h>
// remember to set compilation option -pthread
void *busy(void *ptr) {
// ptr will point to "Hi"
puts("Hello World");
return NULL;
}
int main() {
pthread_t id;
pthread_create(&id, NULL, busy, "Hi");
while (1) {} // Loop forever
}
如果我们想等待我们的线程完成使用pthread_join
void *result;
pthread_join(id, &result);
在上面的例子中,result
将是null
,因为 busy 函数返回null
。我们需要传递结果地址,因为pthread_join
将写入指针的内容。