音视频:07.linux系统-线程和多线程同步

1.线程的本质

线程与进程的区别:线程大家可以简单理解为一个轻量级的进程,线程共享了栈和堆(变量),没有复制 0-3 G 的进程空间,
但线程会有自己的工作空间,会有自己的 pcb 块。跟 Java 是类似的。

  1. linux线程执行和windows不同,pthread有两种状态joinable状态和unjoinable状态,
    如果线程是joinable状态,当线程函数自己返回退出时或pthread_exit时都不会释放线程所占用堆栈和线程描述符(总计8K多)。只有当你调用了pthread_join之后这些资源才会被释放。
    若是unjoinable状态的线程,这些资源在线程函数退出时或pthread_exit时自动会被释放。
  2. unjoinable属性可以在pthread_create时指定,或在线程创建后在线程中pthread_detach自己, 如:pthread_detach(pthread_self()),将状态改为unjoinable状态,确保资源的释放。或者将线程置为 joinable,然后适时调用pthread_join.
  3. 其实简单的说就是在线程函数头加上 pthread_detach(pthread_self())的话,线程状态改变,在函数尾部直接 pthread_exit线程就会自动退出。省去了给线程擦屁股的麻烦。

2.创建线程

pthread_create(&tid,NULL,thread_run,NULL);

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 <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

// void* 是可以任意类型,指针、变量都可以
void* thread_run(void* arg){
printf("i am a thread\n");
return NULL;
}

int main(){
// 线程创建

/*int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);*/
pthread_t tid;
pthread_create(&tid,NULL,thread_run,NULL);
printf("i am main thread\n");
sleep(1);
return 0;
}

// 编译时调用 gcc thread1.cpp -o thread1 -lpthread // -l 链接 pthread 的库

3.退出线程

pthread_exit(-1);

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
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

void print(int no){
// 碰到 2 不打印
if(no == 2){
// 退出线程
pthread_exit(-1);
// return;
}
printf("i am a thread,no = %d\n",no);
}

// void* 是可以任意类型,指针、变量都可以
void* thread_run(void* arg){
int no = (int)arg;
print(no);
return 0;
}

int main(){
// 线程创建

/*int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);*/
pthread_t tid;
for(int i=0; i<5; i++){
pthread_create(&tid,NULL,thread_run,(void*)i);
}
printf("i am main thread\n");
sleep(1);
return 0;
}

// 编译时调用 gcc thread1.cpp -o thread1 -lpthread // -l 链接 pthread 的库

4.回收线程

pthread_join(tid,&retval); // retval = 0代表回收成功,-1 代表线程已经退出了

pthread_join()即是子线程合入主线程,主线程阻塞等待子线程结束,然后回收子线程资源。

pthread_join()函数,以阻塞的方式等待thread指定的线程结束。当函数返回时,被等待线程的资源被收回。如果线程已经结束,那么该函数会立即返回。并且thread指定的线程必须是joinable的。

一般情况下需要回收线程,retval 用来接收线程的返回值,tid 是线程的id,该方法会阻塞等待
什么情况下不需要回收呢?除非调用了分离线程

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
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

void print(int no){
// 碰到 2 不打印
if(no == 2){
// 退出线程
pthread_exit(-1);
// return;
}
printf("i am a thread,no = %d\n",no);
}

// void* 是可以任意类型,指针、变量都可以
void* thread_run(void* arg){
int no = (int)arg;
print(no);
return 0;
}

int main(){
// 线程创建

/*int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);*/
pthread_t tid;
pthread_create(&tid,NULL,thread_run,2);
printf("i am main thread\n");
// linux 中需要回收线程,等待回收
/*int pthread_join(pthread_t thread, void **retval);*/
int *retval;
pthread_join(tid,&retval);
printf("retval = %d\n",retval);
// sleep(1);
return 0;
}

// 编译时调用 gcc thread1.cpp -o thread1 -lpthread // -l 链接 pthread 的库

5.杀死(取消)线程

pthread_cancel(tid); // 取消线程需要有函数进入内核,也就是说需要调用系统函数

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
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

void print(int no){
// 碰到 2 不打印
if(no == 2){
// 退出线程
pthread_exit(-1);
// return;
}
while(1){
// 循环里面是空的,则会卡死,线程不会取消,原因是取消线程需要有函数进入内核
// printf("i am a thread,no = %d\n",no);

// 实在没什么写的,又想线程能够取消,可以调下面这个方法
pthread_testcancel();
}
}

// void* 是可以任意类型,指针、变量都可以
void* thread_run(void* arg){
int no = (int)arg;
print(no);
return 0;
}

int main(){
// 线程创建

/*int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);*/
pthread_t tid;
pthread_create(&tid,NULL,thread_run,1);
printf("i am main thread\n");
// linux 中需要回收线程,等待回收
/*int pthread_join(pthread_t thread, void **retval);*/
int *retval;
sleep(5);
pthread_cancel(tid);
pthread_join(tid,&retval);// 返回值是 -1 ,表示被干掉或者退出的线程
printf("retval = %d\n",retval);
return 0;
}

// 编译时调用 gcc thread1.cpp -o thread1 -lpthread // -l 链接 pthread 的库

6.分离线程

pthread_detach(tid);使线程ID为tid的线程处于分离状态,一旦线程处于分离状态,该线程终止时底 层资源立即被回收

分离线程之后是当线程执行完毕或者pthread_exit后,残留在线程中的资源会自动回收,也就是说线程需要回收,有两种方式一种是 join 一种是 detach 分离。

该函数不会阻塞父线程。pthread_detach(tid);函数用于只是应用程序在线程tid终止时回收其存储空间。如果tid尚未终止,pthread_detach()不会终止该线程。

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
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

void print(int no){
// 碰到 2 不打印
if(no == 2){
// 退出线程
pthread_exit(-1);
// return;
}
printf("i am a thread,no = %d\n",no);
sleep(2);
}

// void* 是可以任意类型,指针、变量都可以
void* thread_run(void* arg){
int no = (int)arg;
print(no);
return 0;
}

int main(){
// 线程创建

/*int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);*/
pthread_t tid;
pthread_create(&tid,NULL,thread_run,1);
// 对线程进行分离
pthread_detach(tid);
printf("i am main thread\n");
// linux 中需要回收线程,等待回收
/*int pthread_join(pthread_t thread, void **retval);*/
int *retval;
pthread_join(tid,&retval);// 返回值是 -1 ,表示被干掉或者退出的线程
printf("retval = %d\n",retval);
sleep(3);

return 0;
}

// 编译时调用 gcc thread1.cpp -o thread1 -lpthread // -l 链接 pthread 的库

7.线程同步

  • pthread_mutex_t mutex
  • 加锁:pthread_mutex_lock(&mutex);
  • 解锁:pthread_mutex_unlock(&mutex);
  • 销毁锁:pthread_mutex_destroy(&mutex);
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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

long number = 0;
// 加一个锁
pthread_mutex_t mutex;
// 加一个条件
pthread_cond_t product_cond;

void* thread_run(void* arg){
for(long i=0; i<200000000;i++){
// 加锁
pthread_mutex_lock(&mutex);
number++;
// 解锁
pthread_mutex_unlock(&mutex);
}
return 0;
}

// 出现加锁的出错的情况,应该是没有计算完毕

// 生成者与消费者(java要能手写)
void* consumer(void *arg){
for(;;){
pthread_mutex_lock(&mutex);
// 1. 阻塞等待唤醒
// 2. 释放锁
// 3. 被唤醒,解除阻塞,需要重新竞争锁
// 是一个 while 循环,等待有两种方式会被唤醒,一种是条件发信号,一种是系统(不正常)惊群效应
while(number <= 0){
printf("等待生产者生成产品");
pthread_cond_wait(&product_cond, &mutex);
}
printf("消费者消费产品: %ld\n",number);
number--;
// 解锁
pthread_mutex_unlock(&mutex);
sleep(1);
}
return (void*)0;
}
void* producer(void *arg){
for(;;){
pthread_mutex_lock(&mutex);
number++;
printf("生成者生产产品: %ld\n",number);
// 通知消费者消费
pthread_cond_signal(&product_cond);
// 解锁
pthread_mutex_unlock(&mutex);
sleep(1);
}
return (void*)0;
}

int main(){
// 初始化锁
pthread_mutex_init(&mutex,NULL);
pthread_cond_init(&product_cond,NULL);
pthread_t tid;
int *retval;
//for(int i=0; i<4;i++){
// 传的是同一个 id ,tid 没有赋值的,tid 是当做一个传出参数 0
pthread_create(&tid, NULL, producer, NULL);
pthread_detach(tid);
pthread_create(&tid, NULL, consumer, NULL);
pthread_detach(tid);
pthread_create(&tid, NULL, consumer, NULL);
pthread_detach(tid);
//}
while(1){

}
// sleep(5);
// 销毁锁
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&product_cond);
// 销毁条件
printf("number = %ld\n", number);
return 0;
}
-------------本文结束感谢您的阅读-------------
坚持原创技术分享,您的支持将鼓励我继续创作!
0%