pthread多线程: 创建最简单的线程


在这里插入图片描述

1. 目的

Pthread 提供了 Linux / MacOSX 等系统下的一套多线程 API。使用多线程可以用于提升 CPU 利用率, 加速任务执行等。

1.1 不使用 Pthread 的情况

如果要使用多线程, 但是又不使用 Pthread, 我只能想到如下几种情况:

  1. 限定在 Windows 操作系统上,使用 Windows 的那一套多线程 API
  2. 使用 std::thread, 坚定的 modern C++, pure C++ 信仰
  3. 有一套封装好了的 API, 能支持当前系统, 例如封装了 Pthread 和 Windows thread, 甚至封装了 std::thread

1.2 使用 Pthread 的情况

实际上 Pthread 在非常多的 操作系统 x 编译器 x 硬件平台 组合条件下可以使用:

  • Linux x64 环境: 例如服务端编程,搞Web后端网络开发的
  • 深度学习加速,当然可能 openmp 也基本够用
  • Android 和 TDA4 这样的嵌入式设备上,对应到手机、车载两大消费者平台

1.3 使用 Pthread 的好处

使用 Pthread 还有额外的好处:

  • 能使用 std::thread, 大概率也能使用 Pthread,Windows 上有 Windows-Pthreads 库可以使用
  • Pthread 是 C 接口, 纯 C 环境仍然可以使用
  • Pthread 的例子很多, 开源代码也很多,甚至可能比 std::thread 的资料和例子多不止一倍

以上,是我学习使用 Pthread 的主要原因。

2. Pthread 创建线程的 API

2.1 环境

使用 ubuntu 22.04。它自带的 man 可以查询 pthread 的 API。

2.2 pthread_create()

man pthread_create
PTHREAD_CREATE(3)                                                 Linux Programmer's Manual                                                PTHREAD_CREATE(3)

NAME
       pthread_create - create a new thread

SYNOPSIS
       #include <pthread.h>

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

       Compile and link with -pthread.

2.3pthread_join()

这个API用于在当前线程等待子线程的结束。

PTHREAD_JOIN(3)                                                   Linux Programmer's Manual                                                  PTHREAD_JOIN(3)

NAME
       pthread_join - join with a terminated thread

SYNOPSIS
       #include <pthread.h>

       int pthread_join(pthread_t thread, void **retval);

       Compile and link with -pthread.

3. 创建最简单的线程

3.1 要点

何为最简单?能省则省, 能用默认就用默认。换言之使用最小必要条件创建一个子线程。

要点:

  • 子线程需要执行一个函数,需要实现这个函数 f (命名就很简洁)
  • 函数 f 需要返回 void* 类型
  • 函数 f 需要传入 void* 类型参数
  • 创建线程, 需要调用 pthread_create() 函数
  • 主线程需要等待线程结束,调用 pthread_join() 函数
  • 只创建一个子线程

3.2 代码

按如上要点很容易写出如下实现:
simplest.cpp

//
// 最简单的 pthread 创建线程的例子: 创建一个线程。线程参数和线程属性都用NULL
//

#include <pthread.h>
#include <stdio.h>

// 1. 线程函数返回值, 必须是 void*
// 2. 线程函数参数, 必须只有一个, 类型必须是 void*
void* f(void*)
{
    printf("This is thread function\n");
    return NULL;
}

int main()
{
    pthread_t t; // 3. 创建线程对象变量
    pthread_create(&t, NULL, f, NULL); // 4. 创建线程: 传入线程对象(参数1), 传入线程函数(参数3)。

    pthread_join(t, NULL); // 等待线程结束

    return 0;
}

编译并运行:

zz@Legion-R7000P% clang++ simplest.cpp 
zz@Legion-R7000P% ./a.out             
This is thread function

可以看到, 函数 f 被执行, 而 f 并不是按常规的 f() 方式显式调用,而是一种回调(callback)方式调用的。

4. 创建多个子线程

4.1 要点

基于前一节的代码,这次创建4个线程。创建线程、等待子线程结束的两段代码,各自用 for 循环包裹起来即可实现。

4.2 代码

multiple_threads.cpp:

//
// 创建多个线程。线程属性和线程参数仍然用NULL。
//

#include <pthread.h>
#include <stdio.h>

void* hello(void*)
{
    printf("This is hello\n");
    return NULL;
}

int main()
{
    const int N = 4;
    pthread_t t[N];
    for (int i = 0; i < N; i++)
    {
        pthread_create(&t[i], NULL, hello, NULL);
    }
    
    for (int i = 0; i < N; i++)
    {
        pthread_join(t[i], NULL);
    }

    return 0;
}

编译并运行:

zz@Legion-R7000P% clang++ multiple_threads.cpp 
zz@Legion-R7000P% ./a.out 
This is hello
This is hello
This is hello
This is hello

可以看到, 输出了4句相同的 This is hello, 说明确实运行了4次 f 函数, 而且主线程并没有显式调用 f(), 而是一种回调方式, 通过子线程来实现的调用。

5. 总结

这里的例子很简单, 说是简陋也不为过:

  • 参数是 NULL,没有传递有效参数
  • 各个线程之间独立, 没有展示互相依赖的情况
  • 主线程等待子线程结束, 没有考虑不需要等待的情况
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值