C语言中的多线程


什么是线程?
线程是进程中的单个序列流。因为线程具有进程的一些属性,所以它们有时被称为轻量级进程。

进程和线程有什么区别?
线程并不独立于其他类似的进程,因此线程与其他线程共享其代码段、数据段和操作系统资源,如打开的文件和信号。但是,与进程一样,线程也有自己的程序计数器(PC)、寄存器集和堆栈空间。

为什么选择多线程?

线程是并行运行的,可以提高应用程序效率。例如,在浏览器中,多个选项卡可以是不同的线程。MS Word使用多个线程,一个线程来格式化文本,另一个线程来处理输入等等。
线程的运行速度比进程快,原因如下:

  • 线程创建速度快得多
  • 线程之间的上下文切换要快得多
  • 线程可以轻松终止
  • 线程之间的通信更快

查看 更多信息

我们可以用C写多线程程序吗?
与Java不同,C语言标准不支持多线程。POSIX线程(或Pthread)是线程的POSIX标准。pthread的实现可通过GCC编译器获得。

例子1

请注意:下面的程序只能用带有pthread库的C编译器编译

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//  multithread.c -- 演示pthread基本函数用法的简单C程序
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>

// 作为线程执行的普通C函数
void *myThreadFun(void *vargp)
{
    sleep(1);
    printf("来自线程的消息
");
    return NULL;
}

int main()
{
    pthread_t thread_id;
    printf("线程之前
");
    pthread_create(&thread_id, NULL, myThreadFun, NULL);
    pthread_join(thread_id, NULL);
    printf("线程之后
");
    exit(0);
}

要编译程序,需要链接到 pthread 库,所以编译命令如下

1
gcc multithread.c -lpthread

输出结果

1
2
3
线程之前
来自线程的消息
线程之后

在main()中,我们声明了一个名为thread_id的变量,其类型为pthread_t,这是一个用于标识系统中线程的整数
在声明thread_id之后,我们调用pthread_create()函数来创建线程

  • 第一个参数是指向由该函数设置的thread_id的指针
  • 第二个参数指定属性。如果值为NULL,则应使用默认属性
  • 第三个参数是要为要创建的线程执行的函数的名称
  • 第四个参数用于将参数传递给函数myThreadFun
  • 线程的pthread_join()函数相当于进程的wait()。对 pthread_join 的调用将阻塞调用线程,直到标识符等于第一个参数的线程终止

例子2

如上所述,所有线程共享数据段。全局变量和静态变量存储在数据段中。因此,它们由所有线程共享。下面的示例程序演示了相同的情况

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// 显示具有全局变量和静态变量的多线程的C程序。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>

// 创建一个全局变量来在线程中更改它
int g = 0;


// 这个函数将被所有线程执行
void *myThreadFun(void *vargp)
{
    // 存储传递给此线程的value参数
    int *myid = (int *)vargp;

    // 创建一个静态变量来观察它的变化
    static int s = 0;

    // 更改静态和全局变量
    ++s; ++g;

    // 打印参数、静态变量和全局变量
    printf("Thread ID: %d, Static: %d, Global: %d
", *myid, s, g);
}

int main()
{
    int i;
    pthread_t tid;

    // 创建三个线程,使用同一个线程函数
    for (i = 0; i < 3; i++)
        pthread_create(&tid, NULL, myThreadFun, (void *)&tid);

    pthread_exit(NULL);
    return 0;
}

输出结果

1
2
3
Thread ID: -1164859648, Static: 1, Global: 1
Thread ID: -1173252352, Static: 2, Global: 2
Thread ID: -1173252352, Static: 3, Global: 3

可以看到,各个线程共享了主进程的全局变量和静态变量

请注意: 上面的例子来只是用于展示线程是如何工作的。在线程中的访问全局变量通常不是一个好主意。如果线程2的优先级高于线程1,而线程1需要更改变量,该怎么办?在实践中,如果需要由多个线程访问同一个全局变量,那么应该使用互斥来访问它们。

更多的例子 :pthread_cancel()、pthread_self() 函数