如何在 C 中创建 Linux 线程

在 Linux 上,您可以使用 POSIX 线程 (pthread) 库在 C/C++ 中创建和管理线程。与其他操作系统不同,Linux 中的线程和进程之间几乎没有区别。这就是 Linux 经常将其线程称为轻量级进程的原因。

使用 pthread 库,您可以创建线程、等待它们终止以及显式终止它们。

Linux 上线程使用的历史

在 Linux 2.6 版本之前,主要的线程实现是 LinuxThreads。这种实现在性能和同步操作方面有很大的限制。可以运行的最大线程数的限制将它们限制在 1000 个以内。

2003 年,由来自 IBM 和 RedHat 的开发人员领导的团队成功地使Native POSIX Thread Library (NPTL) 项目可用。它首先在 RedHat Enterprise 版本 3 中引入,以解决 Linux 上 Java 虚拟机的性能问题。今天,GNU C 库(GNU C Library)包含这两种线程机制的实现。

这些都不是绿色线程的实现,虚拟机将在纯用户模式下管理和运行。当您使用 pthread 库时,内核会在每次程序启动时创建一个线程。

您可以在 /proc/<PID>/task 下的文件中找到任何正在运行的进程的线程特定信息。这是 procfs Linux 标准下进程信息的标准位置。对于单线程应用,会出现该目录下有一条与PID值相同的任务记录。

线程的工作逻辑

线程就像当前在操作系统上运行的进程。在单处理器系统(例如微控制器)中,操作系统内核模拟线程。这允许事务通过切片同时运行。

单核操作系统一次只能真正运行一个进程。但是,在多核或多处理器系统中,这些进程可以同时运行。

C中的线程创建

您可以使用 pthread_create 函数创建一个新线程。pthread.h 头文件包括其签名定义以及其他与线程相关的函数。线程使用与主程序相同的地址空间和文件描述符。

pthread 库还包括对同步操作所需的互斥锁和条件操作的必要支持。

当您使用 pthread 库的函数时,您必须确保编译器将pthread库链接到您的可执行文件中。如有必要,您可以使用-l选项指示编译器链接到库:

linuxmi@linuxmi:~/www.linuxmi.com$ gcc -o linuxmi linuxmi.c -lpthread
linuxmi@linuxmi:~/www.linuxmi.com$ ./linuxmi

pthread_create 函数具有以下签名:

int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)

如果程序成功,它返回 0。如果有问题,它会返回一个非零错误代码。在上面的函数签名中:

  • thread参数的类型为pthread_t。创建的线程将始终可以使用此引用访问。
  • attr参数允许您指定自定义行为。您可以使用一系列以pthread_attr_开头的特定于线程的函数来设置该值。可能的自定义是调度策略、堆栈大小和分离策略。
  • start_routine指定线程将运行的函数。
  • arg表示由线程传递给函数的通用数据结构。

这是一个示例应用程序:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
 
void *worker(void *data)
{
  char *name = (char*)data;
 
  for (int i = 0; i < 120; i++)
  {
    usleep(50000);
    printf("Hi from thread name = %s/n", name);
  }
 
  printf("Thread %s done!/n", name);
  return NULL;
}
 
int main(void)
{
  pthread_t th1, th2;
  pthread_create(&th1, NULL, worker, "X");
  pthread_create(&th2, NULL, worker, "Y");
  sleep(5);
  printf("Exiting from main program/n");
  return 0;
}

线程类型

当线程从应用程序中的 main() 函数返回时,所有线程都会终止,系统会释放该程序使用的所有资源。同样,当使用 exit() 之类的命令退出任何线程时,您的程序将终止所有线程。

使用 pthread_join 函数,您可以等待线程终止。使用此函数的线程将阻塞,直到预期的线程终止。他们从系统中使用的资源不会返回,即使在可连接线程终止、CPU 未调度甚至无法使用 ptread_join 连接等情况下也是如此。

有时会出现使用 pthread_join 加入没有意义的情况;例如,如果无法预测线程何时结束。在这种情况下,您可以确保系统在线程返回时自动返回所有资源。

为此,您应该以 DETACHED 状态启动相关线程。启动线程时,可以通过线程属性值或使用 pthread_detach 函数设置 DETACH 状态

int pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
int pthread_detach(pthread_t thread);

下面是 pthread_join() 的使用示例。将第一个程序中的 main 函数替换为以下内容:

int main(void)
{
  pthread_t th1, th2;
  pthread_create(&th1, NULL, worker, "X");
  pthread_create(&th2, NULL, worker, "Y");
  sleep(5);
  printf("exiting from main program/n");
  pthread_join(th1, NULL);
  pthread_join(th2, NULL);
  return 0;
}

当您编译并运行该程序时,您的输出将是:

Hi from thread Y
Hi from thread X
Hi from thread Y
...
Hi from thread Y
exiting from main program
Hi from thread X
...
Hi from thread X
Thread X done!
Hi from thread Y
Thread Y done!

线程终止

您可以通过调用 pthread_cancel 并传递相应的 pthread_t id 来取消线程:

int pthread_cancel(pthread_t thread);

您可以在以下代码中看到这一点。同样,只有 main 函数不同:

int main(void)
{
  pthread_t th1, th2;
  pthread_create(&th1, NULL, worker, "X");
  pthread_create(&th2, NULL, worker, "Y");
  sleep(1);
  printf("====> Cancelling Thread Y!!/n");
  pthread_cancel(th2);
  usleep(100000);
  printf("====> Cancelling Thread X!/n");
  pthread_cancel(th1);
  printf("exiting from main program/n");
  return 0;
}

为什么要创建线程?

操作系统总是尝试在一个或多个 CPU 上运行线程,要么来自自创建的列表,要么来自用户创建的线程列表。某些线程无法运行,因为它们正在等待来自硬件的输入/输出信号。它们也可能是自愿等待,等待另一个线程的响应,或者有另一个线程阻塞它们。

您可以调整分配给使用 pthread 创建的线程的资源。这可以是自定义调度策略,或者您可以根据需要选择调度算法,例如 FIFO 或 Round-robin。

The post 如何在 C 中创建 Linux 线程 first appeared on Linux迷.

版权声明:
作者:cc
链接:https://www.techfm.club/p/38095.html
来源:TechFM
文章版权归作者所有,未经允许请勿转载。

THE END
分享
二维码
< <上一篇
下一篇>>