前言:
在编写代码时,是否会遇到以下的场景会感觉到难以下手?
要做 2 件事,一件需要阻塞等待,另一件需要实时进行。例如播放器:一边在屏幕上播放视频,一边在等待用户的按键操作。如果使用单线程的话,程序必须一会查询有无按键,一会播放视频。查询按键太久,就会导致视频播放卡顿; 视频播放太久,就无法及时响应用户的操作。并且查询按键和播放视频的代码混 杂在一起,代码丑陋。如果使用多线程,线程1单独处理按键,线程 2 单独处理播放,可以完美解决上述问题。
目录
线程的使用
1.线程概念
2.线程的标识 pthread_t
3.线程的创建
4.线程的接收
5.互斥访问
6.同步操作
多进程的原理逻辑,需要俩个进行进行通信,效率低
多线程的原理逻辑, 俩个线程之间特别方便的进行传输通信,效率高
注意:调度是以线程为单位的,资源分配是以进程为单位的。
所谓线程,就是操作系统所能调度的最小单位。普通的进程,只有一个线程在执行对应的逻辑。我们可以通过多线程编程,使一个进程可以去执行多个不同的任务。相比多进程编程而言,线程享有共享资源,即在进程中出现的全局变量, 每个线程都可以去访问它,与进程共享“4G”内存空间,使得系统资源消耗减少。我们主要学习研究 Linux 下 POSIX 线程。
对于进程而言,每一个进程都有一个唯一对应的 PID 号来表示该进程,而对于线程而言,也有一个“类似于进程的 PID 号”,名为 tid,其本质是一个pthread_t 类型的变量。线程号与进程号是表示线程和进程的唯一标识,但是对于线程号而言,其仅仅在其所属的进程上下文中才有意义。
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
第一个参数pthread_t *thread为 pthread_t 指针,用来保存新建线程的线程号;
第二个参数const pthread_attr_t *attr表示了线程的属性,一般传入NULL 表示默认属性; 第三个参数void *(*start_routine) (void *)是一个函数指针,就是线程执行的函数。这个函数返回值为 void*, 形参为 void*。
第四个参数void *arg则表示为向线程处理函数传入的参数,若不传入,可用 NULL 填充
1 #include
2 #include
3 #include
4
5
6 static void *my_thread_func (void *data)
7 {
8 while (1)
9 {
10 sleep(1);
11 }
12 }
13
14
15 int main(int argc, char **argv)
16 {
17 pthread_t tid;
18 int ret;
19
20 /* 1. 创建"接收线程" */
21 ret = pthread_create(&tid, NULL, my_thread_func, NULL);
22 if (ret)
23 {
24 printf("pthread_create err!\n");
25 return -1;
26 }
27
28
29 /* 2. 主线程读取标准输入, 发给"接收线程" */
30 while (1)
31 {
32 sleep(1);
33 }
34 return 0;
35 }
第6~12行: 定义一个结构体函数
第17行: 定义线程ID
第18行: 接收第21行pthread_create返回值
book@100ask:~/source/13_thread/02_视频配套源码$ gcc -o pthread1 pthread1.c -lpthread
char *fgets(char *s, int size, FILE *stream); //用于读取字符串
int sem_init(sem_t *sem, int pshared, unsigned int value);
// 初始化
#include <semaphore.h>
int sem_init(sem_t *sem,int pshared,unsigned int value);
等待/释放:
#include <pthread.h>
int sem_wait(sem_t *sem);
int sem_post(sem_t *sem);
1 #include
2 #include
3 #include
4 #include
5
6 static char g_buf[1000];
7 static sem_t g_sem;
8 static void *my_thread_func (void *data)
9 {
10 while (1)
11 {
12 //sleep(1);
13 /* 等待通知 */
14 //while (g_hasData == 0);
15 sem_wait(&g_sem);
16
17 /* 打印 */
18 printf("recv: %s\n", g_buf);
19 }
20
21 return NULL;
22 }
23
24
25 int main(int argc, char **argv)
26 {
27 pthread_t tid;
28 int ret;
29
30 sem_init(&g_sem, 0, 0);
31
32 /* 1. 创建"接收线程" */
33 ret = pthread_create(&tid, NULL, my_thread_func, NULL);
34 if (ret)
35 {
36 printf("pthread_create err!\n");
37 return -1;
38 }
39
40
41 /* 2. 主线程读取标准输入, 发给"接收线程" */
42 while (1)
43 {
44 fgets(g_buf, 1000, stdin);
45
46 /* 通知接收线程 */
47 sem_post(&g_sem);
48 }
49 return 0;
50 }
第6行: 设置全局变量
第7行: 设置标记位
使用信号量进行同步操作
book@100ask:~/source/13_thread/02_视频配套源码$ gcc -o pthread3 pthread3.c -lpthread
打印信息的过程中可能出现前半端打印新数据,后半段打印旧数据的情况,所以为了防止这种现象的发生,我们选择加入互斥量进行优化。
1 #include
2 #include
3 #include
4 #include
5 #include
6
7 static char g_buf[1000];
8 static sem_t g_sem;
9 static pthread_mutex_t g_tMutex = PTHREAD_MUTEX_INITIALIZER;
10
11 static void *my_thread_func (void *data)
12 {
13 while (1)
14 {
15 //sleep(1);
16 /* 等待通知 */
17 //while (g_hasData == 0);
18 sem_wait(&g_sem);
19
20 /* 打印 */
21 pthread_mutex_lock(&g_tMutex);
22 printf("recv: %s\n", g_buf);
23 pthread_mutex_unlock(&g_tMutex);
24 }
25
26 return NULL;
27 }
28
29
30 int main(int argc, char **argv)
31 {
32 pthread_t tid;
33 int ret;
34 char buf[1000];
35
36 sem_init(&g_sem, 0, 0);
37
38 /* 1. 创建"接收线程" */
39 ret = pthread_create(&tid, NULL, my_thread_func, NULL);
40 if (ret)
41 {
42 printf("pthread_create err!\n");
43 return -1;
44 }
45
46
47 /* 2. 主线程读取标准输入, 发给"接收线程" */
48 while (1)
49 {
50 fgets(buf, 1000, stdin);
51 pthread_mutex_lock(&g_tMutex);
52 memcpy(g_buf, buf, 1000);
53 pthread_mutex_unlock(&g_tMutex);
54
55 /* 通知接收线程 */
56 sem_post(&g_sem);
57 }
58 return 0;
59 }
第9行: 定义一个互斥量
第21~23和51~53行分别设置子线程和父线程的互斥锁
book@100ask:~/source/13_thread/02_视频配套源码$ gcc -o pthread4 pthread4.c -lpthread
1 #include
2 #include
3 #include
4 #include
5 #include
6
7 static char g_buf[1000];
8 static pthread_mutex_t g_tMutex = PTHREAD_MUTEX_INITIALIZER;
9 static pthread_cond_t g_tConVar = PTHREAD_COND_INITIALIZER;
10
11 static void *my_thread_func (void *data)
12 {
13 while (1)
14 {
15 //sleep(1);
16 /* 等待通知 */
17 //while (g_hasData == 0);
18 pthread_mutex_lock(&g_tMutex);
19 pthread_cond_wait(&g_tConVar, &g_tMutex);
20
21 /* 打印 */
22 printf("recv: %s\n", g_buf);
23 pthread_mutex_unlock(&g_tMutex);
24 }
25
26 return NULL;
27 }
28
29
30 int main(int argc, char **argv)
31 {
32 pthread_t tid;
33 int ret;
34 char buf[1000];
35
36 /* 1. 创建"接收线程" */
37 ret = pthread_create(&tid, NULL, my_thread_func, NULL);
38 if (ret)
39 {
40 printf("pthread_create err!\n");
41 return -1;
42 }
43
44
45 /* 2. 主线程读取标准输入, 发给"接收线程" */
46 while (1)
47 {
48 fgets(buf, 1000, stdin);
49 pthread_mutex_lock(&g_tMutex);
50 memcpy(g_buf, buf, 1000);
51 pthread_cond_signal(&g_tConVar); /* 通知接收线程 */
52 pthread_mutex_unlock(&g_tMutex);
53 }
54 return 0;
55 }
第18和19行:线程A:等待条件成立
第52行:操作临界资源
第51行:线程B:唤醒等待g_tConVar的线程