如何在 Linux 中创建线程

举报
Tiamo_T 发表于 2022/06/13 16:12:16 2022/06/13
【摘要】 在本文中,我们将重点介绍如何创建和识别线程。我们还将提供一个有效的 C 程序示例,该示例将解释如何进行基本的线程编程。线程识别正如通过进程 ID 标识进程一样,线程也由线程 ID 标识。但有趣的是,两者的相似之处就到此为止了。进程 ID 在整个系统中是唯一的,而线程 ID 仅在单个进程的上下文中是唯一的。进程 ID 是整数值,但线程 ID 不一定是整数值。它很可能是一个结构进程 ID 可以很...

在本文中,我们将重点介绍如何创建和识别线程。我们还将提供一个有效的 C 程序示例,该示例将解释如何进行基本的线程编程。


线程识别

正如通过进程 ID 标识进程一样,线程也由线程 ID 标识。但有趣的是,两者的相似之处就到此为止了。

  • 进程 ID 在整个系统中是唯一的,而线程 ID 仅在单个进程的上下文中是唯一的。
  • 进程 ID 是整数值,但线程 ID 不一定是整数值。它很可能是一个结构
  • 进程 ID 可以很容易地打印,而线程 ID 则不容易打印。

以上几点给出了关于进程 ID 和线程 ID 之间差异的概念。

线程 ID 由类型“pthread_t”表示。正如我们已经讨论过的,在大多数情况下,这种类型是一种结构,因此必须有一个可以比较两个线程 ID 的函数。

#include <pthread.h>
int pthread_equal(pthread_t tid1,pthread_t tid2);

如您所见,上述函数采用两个线程 ID,如果两个线程 ID 相等则返回非零值,否则返回零。

当一个线程想要知道它自己的线程 ID 时,可能会出现另一种情况。对于这种情况,以下函数提供所需的服务。


#include <pthread.h>
pthread_t pthread_self(void);

所以我们看到函数'pthread_self()'被一个线程用来打印它自己的线程ID。

现在,有人会问需要上述两个功能的情况。假设有一个链接列表包含不同线程的数据的情况。列表中的每个节点都包含一个线程 ID 和相应的数据。现在每当一个线程试图从链表中获取它的数据时,它首先通过调用'pthread_self()'来获取它自己的ID,然后它在每个节点上调用'pthread_equal()'来查看该节点是否包含它的数据.

上面讨论的一般情况的一个示例是主线程获取要处理的作业,然后将它们推送到链接列表中。现在各个工作线程解析链表并提取分配给它们的作业。

线程创建

通常,当程序启动并成为进程时,它会从默认线程开始。所以我们可以说每个进程至少有一个控制线程。进程可以使用以下函数创建额外的线程:

#include <pthread.h>
int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(*start_rtn)(void), void *restrict arg)

上面的函数需要四个参数,让我们先讨论一下它们:

  • 第一个参数是 pthread_t 类型的地址。一旦函数调用成功,其地址作为第一个参数传递的变量将保存新创建线程的线程 ID。
  • 第二个参数可能包含我们希望新线程包含的某些属性。它可能是优先级等。
  • 第三个参数是函数指针。需要记住的是,每个线程都以一个函数开头,并且函数地址作为第三个参数在这里传递,以便内核知道从哪个函数开始线程。
  • 由于函数(其地址在上面的第三个参数中传递)也可以接受一些参数,因此我们可以将这些参数以指向 void 类型的指针的形式传递。现在,为什么选择 void 类型?这是因为如果一个函数接受多个参数,那么这个指针可能是一个指向可能包含这些参数的结构的指针。

一个实用的线程示例

以下是我们尝试使用上面讨论的所有三个函数的示例代码。

#include<stdio.h>
#include<string.h>
#include<pthread.h>
#include<stdlib.h>
#include<unistd.h>

pthread_t tid[2];

void* doSomeThing(void *arg)
{
    unsigned long i = 0;
    pthread_t id = pthread_self();

    if(pthread_equal(id,tid[0]))
    {
        printf("\n First thread processing\n");
    }
    else
    {
        printf("\n Second thread processing\n");
    }

    for(i=0; i<(0xFFFFFFFF);i++);

    return NULL;
}

int main(void)
{
    int i = 0;
    int err;

    while(i < 2)
    {
        err = pthread_create(&(tid[i]), NULL, &doSomeThing, NULL);
        if (err != 0)
            printf("\ncan't create thread :[%s]", strerror(err));
        else
            printf("\n Thread created successfully\n");

        i++;
    }

    sleep(5);
    return 0;
}

所以这段代码的作用是:

  • 它使用 pthread_create() 函数创建两个线程
  • 两个线程的启动功能保持不变。
  • 在函数 'doSomeThing()' 内部,线程使用 pthread_self() 和 pthread_equal() 函数来识别正在执行的线程是创建时的第一个线程还是第二个线程。
  • 此外,在同一个函数 'doSomeThing()' 中运行一个 for 循环,以模拟一些耗时的工作。

现在,当上面的代码运行时,输出如下:

$ ./threads
Thread created successfully
First thread processing
Thread created successfully
Second thread processing

如输出所示,创建第一个线程并开始处理,然后创建第二个线程并开始处理。这里要注意的一点是线程的执行顺序并不总是固定的。这取决于操作系统的调度算法。

注意:本文中的全部解释都是在 Posix 线程上完成的。从类型中可以理解,pthread_t 类型代表 POSIX 线程。如果应用程序想要测试是否支持 POSIX 线程,则应用程序可以使用宏 _POSIX_THREADS 进行编译时测试。要编译包含对 posix API 的调用的代码,请使用编译选项“-pthread”。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。