线程的使用
多进程的原理逻辑,需要俩个进行进行通信,效率低
多线程的原理逻辑, 俩个线程之间特别方便的进行传输通信,效率高
注意:调度是以线程为单位的,资源分配是以进程为单位的。
1.线程概念
所谓线程,就是操作系统所能调度的最小单位。普通的进程,只有一个线程在执行对应的逻辑。我们可以通过多线程编程,使一个进程可以去执行多个不同的任务。相比多进程编程而言,线程享有共享资源,即在进程中出现的全局变量, 每个线程都可以去访问它,与进程共享“4G”内存空间,使得系统资源消耗减少。我们主要学习研究 Linux 下 POSIX 线程。
2.线程的标识 pthread_t
对于进程而言,每一个进程都有一个唯一对应的 PID 号来表示该进程,而对于线程而言,也有一个“类似于进程的 PID 号”,名为 tid,其本质是一个pthread_t 类型的变量。线程号与进程号是表示线程和进程的唯一标识,但是对于线程号而言,其仅仅在其所属的进程上下文中才有意义。
3.线程的创建
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 <pthread.h> 2 #include <stdio.h> 3 #include <unistd.h> 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
4.线程的接收
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 <pthread.h> 2 #include <stdio.h> 3 #include <unistd.h> 4 #include <semaphore.h> 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
5.互斥访问
打印信息的过程中可能出现前半端打印新数据,后半段打印旧数据的情况,所以为了防止这种现象的发生,我们选择加入互斥量进行优化。
1 #include <pthread.h> 2 #include <stdio.h> 3 #include <unistd.h> 4 #include <semaphore.h> 5 #include <string.h> 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
6.同步操作
1 #include <pthread.h> 2 #include <stdio.h> 3 #include <unistd.h> 4 #include <semaphore.h> 5 #include <string.h> 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的线程