C语言-多线程编程

线程

线程(英语:thread)是操作系统能够进行运算调度的最小单位。线程的创建和销毁都是通过操作系统来完成的。

编程模型

单进程单线程模型

通常编程时使用的是单进程单线程模型,程序在运行时通过操作系统创建一个进程,包含一个主线程,当主线程结束后进程结束,操作系统回收资源。

单进程多线程模型

单进程多线程编程是在程序中创建子线程实现,这时创建的线程称为子线程。程序在运行是通过操作系统创建一个进程,包含主线程,主线程会创建子线程来处理任务;但这时主线程结束进程结束,主线程可以通过等待子线程结束再结束主线程退出。

C语言中多线程

C语言中通过pthread库来进行多线程程序编写。

  • pthread_t 声明一个线程ID
  • pthread_create 使用线程ID创建一个线程含有四个参数 :线程ID(pthread_t)、线程属性(pthread_attr_t)、函数指针[void *(*)(void *)]、参数指针(void *)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

void *
thread(void *arg) {
int a = *(int *)arg;
printf("thread : %d\n", a);
return NULL;
}

int
main(int argc, const char *argv[]) {
int count = 10;
pthread_t tids[count];
for(size_t i = 0; i < count; i++)
{
/* code */
int *s = malloc(sizeof(int));
*s = i;
pthread_create(&tids[i], NULL, thread, s);
}
return 0;
}

多线程模型中存在的问题

多线程模型中,不同线程可以访问同一个变量,不同的线程独立执行,如果同时读写一个变量会造成数据错误,因此在多线程模型中需要加入锁来对变量的安全,通常称为线程安全,加的锁名为互斥锁(mutex)。

测试代码逻辑,拥有一个变量balance,两个操作,存、取,当创建1000个线程时同时运行这两个操作会造成最终balance变量不为0.

不加锁代码

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
40
41
42
43
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

static int balance = 0;

void *
deposit(void *args)
{
balance += 10;
return NULL;
}

void *
withdraw(void *args)
{
balance -= 10;
return NULL;
}

void multiThread(void)
{
int n = 1000;
pthread_t tid1[n];
pthread_t tid2[n];
for (size_t i = 0; i < n; i++)
{
pthread_create(&tid1[i], NULL, deposit, NULL);
pthread_create(&tid2[i], NULL, withdraw, NULL);
}
for (size_t i = 0; i < n; i++)
{
pthread_join(tid1[i], NULL);
pthread_join(tid2[i], NULL);
}
}

int
main(int argc, const char *argv[]) {
printf("main start, balance %d\n", balance);
multiThread();
printf("main end, balance %d\n", balance);
}

加锁代码

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
40
41
42
43
44
45
46
47
48
#include <stdio.h>
#include <pthread.h>


static int balance = 0;
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;


void *
deposit(void *args) {
// 互斥锁
// 事务
pthread_mutex_lock(&mutex);
balance += 10;
pthread_mutex_unlock(&mutex);
return NULL;
}

void *
withdraw(void *args) {
pthread_mutex_lock(&mutex);
balance -= 10;
pthread_mutex_unlock(&mutex);
return NULL;
}

void
multiThread(void) {
int n = 1000;
pthread_t tid1[n];
pthread_t tid2[n];
for (size_t i = 0; i < n; i++) {
pthread_create(&tid1[i], NULL, deposit, NULL);
pthread_create(&tid2[i], NULL, withdraw, NULL);
}
for (size_t i = 0; i < n; i++) {
pthread_join(tid1[i], NULL);
pthread_join(tid2[i], NULL);
}
}

int
main(void) {
printf("main start, balance %d\n", balance);
multiThread();
printf("main end, balance %d\n", balance);
return 0;
}