前几天心血来潮,想用C语言复现一下“睡眠排序”,进而了解了C语言中多线程的使用方法,记录如下:

预备知识

进程和线程

进程是对运行时程序的封装,是系统进行资源调度和分配的的基本单位,实现了操作系统的并发。

线程是进程的子任务,是CPU调度和分派的基本单位,用于保证程序的实时性,实现进程内部的并发。

两者的区别

一个线程只能属于一个进程,而一个进程可以有多个线程。线程依赖于进程存在。

进程在执行过程中拥有独立的内存单元,而多个线程共享进程的内存。

C语言中使用多线程

在C语言中使用多线程,需要用到头文件 pthred.h

多线程涉及的几个函数:

  1. 创建线程 pthread_create()

    函数原型:

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

    参数解释如下:

    • *pthread_t thread:传递一个 pthread_t 类型的指针变量,也可以直接传递某个 pthread_t 类型变量的地址。pthread_t 是一种用于表示线程的数据类型,每一个 pthread_t 类型的变量都可以表示一个线程。
    • *const pthread_attr_t attr:用于手动设置新建线程的属性,例如线程的调用策略、线程所能使用的栈内存的大小等。大部分场景中,我们都不需要手动修改线程的属性,将 attr 参数赋值为 NULL,pthread_create() 函数会采用系统默认的属性值创建线程。
    • void *(*start_routine) (void *):以函数指针的方式指明新建线程需要执行的函数,该函数的参数最多有 1 个(可以省略不写),形参和返回值的类型都必须为 void* 类型。void* 类型又称空指针类型,表明指针所指数据的类型是未知的。使用此类型指针时,我们通常需要先对其进行强制类型转换,然后才能正常访问指针指向的数据。
    • *void arg:指定传递给 start_routine 函数的实参,当不需要传递数据时,将 arg 赋值为 NULL 即可。
  2. 结束线程 pthread_exit()

    函数原型

    pthread_exit(void *retval)
    

    参数解释如下:

    • retval 是 void* 类型的指针,可以指向任何类型的数据,它指向的数据将作为线程退出时的返回值。如果线程不需要返回任何数据,将 retval 参数置为 NULL 即可。

    注意:retval 指针不能指向函数内部的局部数据(比如局部变量)。换句话说,pthread_exit() 函数不能返回一个指向局部数据的指针,否则很可能使程序运行结果出错甚至崩溃。

  3. 等待线程 pthread_join()

    函数原型

    pthread_join(pthread_t thread, void **retval)
    

    参数解释如下:

    • thread 参数用于指定接收哪个线程的返回值。
    • retval 参数表示接收到的返回值,如果 thread 线程没有返回值,或者我们不需要接收 thread 线程的返回值,可以将 retval 参数置为 NULL。

    pthread_join() 函数会一直阻塞调用它的线程,直至目标线程执行结束(接收到目标线程的返回值),阻塞状态才会解除。

利用多线程实现“睡眠排序”

源代码:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>	//多线程头文件
#include <unistd.h>		//包含延时函数的头文件

void *sleep_sort(void *args) {
    int *tmp = (int *)args;
    sleep(*tmp);
    printf("%d ", *tmp);
}

int main() {
    int max;
    printf("请输入要排序几个数:");
    scanf("%d", &max);
    int num[max];
    printf("请输入要排序的%d个数:\n", max);
    for(int i = 0; i < max; i++) {
        scanf("%d", &num[i]);
    }
    printf("开始对");
    for(int i = 0; i < max; i++) {
        printf("%d ", num[i]);
    }
    printf("进行排序:\n");
    
    pthread_t th[max];		//创建线程数组并为每个线程传入参数
    for(int i = 0; i < max; i++) {
        pthread_create(&th[i], NULL, (void *)sleep_sort, &num[i]);
    }
    for(int i = 0; i < max; i++) {
        pthread_join(th[i], NULL);	//为每个线程设置等待
    }
    printf("\n");
}

测试运行: