学习参考博文:
Linux多线程编程初探
Linux系统编程学习相关博文
- Linux系统编程——文件编程的学习
- Linux系统编程——进程的学习
- Linux系统编程——进程间通信的学习
- Linux系统编程——网络编程的学习
Linux系统编程——线程的学习
- 一、概述
- 1. 进程与线程的区别
- 2. 使用线程的理由
- 3. 互斥量
- 4. 条件变量
- 二、线程API
- 三、API介绍
- 1. pthread_creat函数
- 2. pthread_exit函数
- 3. pthread_join函数
- 4. pthread_self函数
- 5. pthread_mutex_inti函数
- 6. pthread_mutex_destroy函数
- 7. pthread_mutex_lock函数
- 8. pthread_mutex_unlock函数
- 9. pthread_cond_init函数
- 10. pthread_cond_destroy函数
- 11. pthread_cond_wait函数
- 12. pthread_cond_signal函数
- 四、API的使用例子
- 1. pthread_creat、pthread_exit、pthread_join、pthread_self函数
- 2. pthread_mutex_init、pthread_mutex_destory、pthread_mutex_lock、pthread_mutex_unlock函数
- 3. pthread_cond_init、pthread_cond_destory、pthread_cond_wait、pthread_cond_signal函数
一、概述
常规学习Linux系统编程的内容是复杂且繁多的,不推荐刚开始接触代码的朋友去学习,所以介绍Linux系统编程的目的主要是以应用开发为主。
1. 进程与线程的区别
- 进程——资源分配的最小单位,线程——程序执行的最小单位
- 在面向线程设计的系统中,进程本身不是基本运行单位,而是线程的容器。程序本身只是指令、数据及其组织形式的描述,进程才是程序 (那些指令和数据) 的真正运行实例。(程序是静态的,进程是动态的)
- 一个进程里边可以有多个线程。进程的所有信息对该进程的所有线程都是共享的,包括可执行的程序文本、程序的全局内存和堆内存、栈以及文件描述符。
- 进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。
- 总的来说:进程有独立的地址空间,线程没有单独的地址空间(同一进程内的线程共享进程的地址空间)。
2. 使用线程的理由
- 使用多线程的理由之一是和进程相比,它是一种非常"节俭"的多任务操作方式。我们知道,在Linux系统下,启动一个新的进程必须分配给它独立的地址空间,建立众多的数据表来维护它的代码段、堆栈段和数据段,这是一种"昂贵"的多任务工作方式。而运行于一个进程中的多个线程,它们彼此之间使用相同的地址空间,共享大部分数据,启动一个线程所花费的空间远远小于启动一个进程所花费的空间,而且,线程间彼此切换所需的时间也远远小于进程间切换所需要的时间。据统计,总的说来,一个进程的开销大约是一个线程开销的30倍左右,当然,在具体的系统上,这个数据可能会有较大的区别。
- 使用多线程的理由之二是线程间方便的通信机制。对不同进程来说,它们具有独立的数据空间,要进行数据的传递只能通过通信的方式进行,这种方式不仅费时,而且很不方便。线程则不然,由于同一进程下的线程之间共享数据空间,所以一个线程的数据可以直接为其它线程所用,这不仅快捷,而且方便。当然,数据的共享也带来其他一些问题,有的变量不能同时被两个线程所修改,有的子程序中声明为static的数据更有可能给多线程程序带来灾难性的打击,这些正是编写多线程程序时最需要注意的地方。
3. 互斥量
- 互斥量(mutex)从本质上来说是一把锁,在访问共享资源前对互斥量进行加锁,在访问完成后释放互斥量上的锁。对互斥量进行加锁后,任何其他试图再次对互斥量加锁的线程将会被阻塞直到当前线程释放该互斥锁。如果释放互斥锁时有多个线程阻塞,所有在该互斥锁上的阻塞线程都会变成可运行状态,第一个变为可运行状态的线程可以对互斥量加锁,其他线程将会看到互斥锁依然被锁住,只能回去等待它重新变为可用。在这种方式下,每次只有一个线程可以向前运行。
- 在设计时需要规定所有的线程必须遵守相同的数据访问规则。只有这样,互斥机制才能正常工作。操作系统并不会做数据访问的串行化。如果允许其中的某个线程在没有得到锁的情况下也可以访问共享资源,那么即使其它的线程在使用共享资源前都获取了锁,也还是会出现数据不一致的问题。
- 互斥变量用pthread_mutex_t数据类型表示。在使用互斥变量前必须对它进行初始化,可以把它置为常量PTHREAD_MUTEX_INITIALIZER(只对静态分配的互斥量),也可以通过调用pthread_mutex_init函数进行初始化。如果动态地分配互斥量(例如通过调用malloc函数),那么在释放内存前需要调用pthread_mutex_destroy。
4. 条件变量
- 条件变量是线程另一可用的同步机制。条件变量给多个线程提供了一个会合的场所。条件变量与互斥量一起使用时,允许线程以无竞争的方式等待特定的条件发生。
- 条件本身是由互斥量保护的。线程在改变条件状态前必须首先锁住互斥量,其他线程在获得互斥量之前不会察觉到这种改变,因为必须锁定互斥量以后才能计算条件。
- 条件变量使用之前必须首先初始化,pthread_cond_t数据类型代表的条件变量可以用两种方式进行初始化,可以把常量PTHREAD_COND_INITIALIZER赋给静态分配的条件变量,但是如果条件变量是动态分配的,可以使用pthread_cond_destroy函数对条件变量进行去除初始化(deinitialize)。
二、线程API
在Linux系统中,操作系统提供了一系列的API,详细看下图
1. 线程
创建 pthread_creat()
退出 pthread_exit()
等待 pthread_join()
获取ID pthread_self()
2. 互斥锁
创建 pthread_mutex_init()
销毁 pthread_mutex_destroy()
加锁 pthread_mutex_lock()
解锁 pthread_mutex_unlock()
3. 条件
创建 pthread_cond_init()
销毁 pthread_cond_destroy()
触发 pthread_cond_signal()
广播 pthread_cond_broadcast()
等待 pthread_cond_wait() / pthread_cond_timewait()
三、API介绍
1. pthread_creat函数
#include <pthread.h>int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(*start_rtn)(void *), void *restrict arg);1. 函数功能:创建线程
2. 形参说明:
tidp:线程ID
attr:线程属性。暂可以把它设置为NULL,以创建默认属性的线程。
start_rtn:新创建线程的运行函数
arg:函数运行传递的参数
3. 返回值:成功返回0,失败返回错误编号
2. pthread_exit函数
#include <pthread.h>int pthread_exit(void *rval_ptr);1. 函数功能:线程退出
2. 形参说明:
rival_ptr:退出时返回的数据
3. pthread_join函数
#include <pthread.h>int pthread_join(pthread_t thread, void **rval_ptr);1. 函数功能:调用这个函数的线程将一直阻塞,直到指定的线程调用pthread_exit、从启动例程中返回或者被取消。
2. 形参说明:
thread:线程ID
rival_ptr:线程退出时返回的数据
3. 返回值:成功返回0,失败返回错误编号
4. pthread_self函数
#include <pthread.h>pthread_t pthread_self(void);1. 函数功能:获取线程ID
2. 返回值:调用线程的ID
5. pthread_mutex_inti函数
#include <pthread.h>int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);1. 函数功能:创建互斥锁
2. 形参说明:
mutex:锁ID
attr:锁的属性。默认的属性初始化互斥量,只需把attr设置为NULL。
3. 返回值:成功返回0,失败返回错误编号
6. pthread_mutex_destroy函数
#include <pthread.h>int pthread_mutex_destroy(pthread_mutex_t *mutex);1. 函数功能:销毁互斥锁
2. 形参说明:
mutex:锁ID
3. 返回值:成功返回0,失败返回错误编号
7. pthread_mutex_lock函数
#include <pthread.h>int pthread_mutex_lock(pthread_mutex_t *mutex);1. 函数功能:加锁
2. 形参说明:
mutex:锁ID
3. 返回值:成功返回0,失败返回错误编号
8. pthread_mutex_unlock函数
#include <pthread.h>int pthread_mutex_unlock(pthread_mutex_t *mutex);1. 函数功能:解锁
2. 形参说明:
mutex:锁ID
3. 返回值:成功返回0,失败返回错误编号
9. pthread_cond_init函数
#include <pthread.h>int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);1. 函数功能:创建条件变量
2. 形参说明:
cond:条件ID
attr:条件属性。除非需要创建一个非默认属性的条件变量,否则该参数可以设置为NULL。
3. 返回值:成功返回0,失败返回错误编号
10. pthread_cond_destroy函数
#include <pthread.h>int pthread_cond_destroy(pthread_cond_t *cond);1. 函数功能:销毁条件变量
2. 形参说明:
cond:条件ID
3. 返回值:成功返回0,失败返回错误编号
11. pthread_cond_wait函数
#include <pthread.h>int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);1. 函数功能:等待条件为真
2. 形参说明:
cond:条件ID
mutex:锁ID
3. 返回值:成功返回0,失败返回错误编号
12. pthread_cond_signal函数
#include <pthread.h>int pthread_cond_signal(pthread_cond_t *cond);1. 函数功能:触发条件
2. 形参说明:
cond:条件ID
3. 返回值:成功返回0,失败返回错误编号
四、API的使用例子
1. pthread_creat、pthread_exit、pthread_join、pthread_self函数
1 #include <stdio.h>2 #include <stdlib.h>3 #include <string.h>4 5 #include <pthread.h>6 7 // 1. int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(* start_rtn)(void *), void *restrict arg);8 // 2. pthread_t pthread_self(void);9 // 3. int pthread_join(pthread_t thread, void **rval_ptr);10 // 4. int pthread_exit(void *rval_ptr);11 12 void *func1(void *arg)13 {14 static int data = 10;15 16 printf("t1: %ld\n", pthread_self()); //打印线程ID17 printf("t1: arg = %d\n", *((int *)arg)); //打印传递的参数18 19 pthread_exit((void *)(&data)); //线程退出并返回数据20 }21 22 int main()23 {24 int ret = 0;25 int param = 100;26 int *pret = NULL;27 28 pthread_t t1; //线程ID29 30 ret = pthread_create(&t1, NULL, func1, (void *)¶m); //创建线程31 32 if(ret == 0){33 printf("main: %ld, pthread create success\n", pthread_self());34 }35 36 pthread_join(t1, (void **)(&pret)); //等待线程退出37 38 printf("main: pret = %d\n", *pret); //打印线程退出时反馈的的数据39 40 return 0;41 }
2. pthread_mutex_init、pthread_mutex_destory、pthread_mutex_lock、pthread_mutex_unlock函数
1 #include <stdio.h>2 #include <stdlib.h>3 #include <string.h>4 5 #include <pthread.h>6 7 // 1. int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restric t attr);8 // 2. int pthread_mutex_destroy(pthread_mutex_t *mutex);9 // 3. int pthread_mutex_lock(pthread_mutex_t *mutex);10 // 4. int pthread_mutex_unlock(pthread_mutex_t *mutex);11 12 int g_data = 0;13 14 pthread_mutex_t mutex; //锁15 16 void *func1(void *arg)17 {18 printf("t1: %ld\n", pthread_self()); //打印线程ID19 printf("t1: arg = %d\n", *((int *)arg)); //打印传递过来的数据20 21 pthread_mutex_lock(&mutex); //加锁22 23 while(1){24 printf("t1: %d\n", g_data++);25 26 if(g_data == 3){27 printf("t1 quit===========================\n");28 pthread_mutex_unlock(&mutex); //当g_data = 3时解锁29 pthread_exit(NULL); //线程退出30 }31 32 sleep(1);33 }34 35 }36 37 void *func2(void *arg)38 {39 printf("t2: %ld\n", pthread_self());40 printf("t2: arg = %d\n", *((int *)arg));41 42 while(1){43 pthread_mutex_lock(&mutex); //加锁44 45 printf("t2: %d\n", g_data++);46 47 pthread_mutex_unlock(&mutex); //解锁48 49 sleep(1);50 }51 }52 53 int main()54 {55 int ret = 0;56 int param = 100;57 58 pthread_t t1;59 pthread_t t2;60 61 pthread_mutex_init(&mutex, NULL); //创建互斥锁62 63 ret = pthread_create(&t1, NULL, func1, (void *)¶m); //创建线程164 65 if(ret == 0){66 //printf("main: t1:%ld, pthread create success\n", pthread_self());67 }68 69 ret = pthread_create(&t2, NULL, func2, (void *)¶m); //创建线程270 71 if(ret == 0){72 //printf("main: t2:%ld, pthread create success\n", pthread_self());73 }74 75 pthread_join(t1, NULL); //等待线程1退出76 pthread_join(t2, NULL); //等待线程2退出77 78 pthread_mutex_destroy(&mutex); //销毁互斥锁79 80 return 0;81 }
3. pthread_cond_init、pthread_cond_destory、pthread_cond_wait、pthread_cond_signal函数
1. 动态初始化
1 #include <stdio.h>2 #include <stdlib.h>3 #include <string.h>4 5 #include <pthread.h>6 7 // 1. int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(* start_rtn)(void *), void *restrict arg);8 // 2. pthread_t pthread_self(void);9 // 3. int pthread_join(pthread_t thread, void **rval_ptr);10 // 4. int pthread_exit(void *rval_ptr);11 // 5. int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mut exattr_t *res trict attr);12 // 6. int pthread_mutex_destroy(pthread_mutex_t *mutex);13 // 7. int pthread_mutex_lock(pthread_mutex_t *mutex);14 // 8. int pthread_mutex_unlock(pthread_mutex_t *mutex);15 // 9. int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict at tr);16 // 10. int pthread_cond_destroy(pthread_cond_t *cond);17 // 11. int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);18 // 12. int pthread_cond_signal(pthread_cond_t *cond);19 20 int g_data = 0;21 22 pthread_cond_t cond; //条件变量23 pthread_mutex_t mutex; //锁24 25 void *func1(void *arg)26 {27 printf("t1: %ld\n", pthread_self()); //打印线程ID28 printf("t1: arg = %d\n", *((int *)arg)); //打印传递过来的数据29 30 while(1){31 pthread_cond_wait(&cond, &mutex); //等待条件为真32 33 printf("t1 run=========================\n");34 printf("t1: %d\n", g_data);35 g_data = 0;36 37 sleep(1);38 }39 }40 41 void *func2(void *arg)42 {43 printf("t2: %ld\n", pthread_self());44 printf("t2: arg = %d\n", *((int *)arg));45 46 while(1){47 pthread_mutex_lock(&mutex); //加锁48 49 printf("t2: %d\n", g_data++);50 if(g_data == 3){51 pthread_cond_signal(&cond); //当g_data = 3时,触发条件,唤醒等待该条件的线程52 }53 54 pthread_mutex_unlock(&mutex); //解锁55 56 sleep(1);57 }58 }59 60 int main()61 {62 int ret = 0;63 int param = 100;64 65 pthread_t t1;66 pthread_t t2;67 68 pthread_cond_init(&cond, NULL); //创建条件变量69 pthread_mutex_init(&mutex, NULL); //创建互斥锁70 71 ret = pthread_create(&t1, NULL, func1, (void *)¶m); //创建线程172 73 if(ret == 0){74 //printf("main: t1:%ld, pthread create success\n", pthread_self());75 }76 77 ret = pthread_create(&t2, NULL, func2, (void *)¶m); //创建线程278 79 if(ret == 0){80 //printf("main: t2:%ld, pthread create success\n", pthread_self());81 }82 83 pthread_join(t1, NULL); //等待线程1退出84 pthread_join(t2, NULL); //等待线程2退出85 86 pthread_cond_destroy(&cond); //销毁条件变量87 pthread_mutex_destroy(&mutex); //销毁互斥锁88 89 return 0;90 }
2. 静态初始化
1 #include <stdio.h>2 #include <stdlib.h>3 #include <string.h>4 5 #include <pthread.h>6 7 // 1. int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(* start_rtn)(void *), void *restrict arg);8 // 2. pthread_t pthread_self(void);9 // 3. int pthread_join(pthread_t thread, void **rval_ptr);10 // 4. int pthread_exit(void *rval_ptr);11 // 5. int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mut exattr_t *res trict attr);12 // 6. int pthread_mutex_destroy(pthread_mutex_t *mutex);13 // 7. int pthread_mutex_lock(pthread_mutex_t *mutex);14 // 8. int pthread_mutex_unlock(pthread_mutex_t *mutex);15 // 9. int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict at tr);16 // 10. int pthread_cond_destroy(pthread_cond_t *cond);17 // 11. int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);18 // 12. int pthread_cond_signal(pthread_cond_t *cond);19 20 int g_data = 0;21 22 pthread_cond_t cond = PTHREAD_COND_INITIALIZER; //初始化条件变量23 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; //初始化互斥锁24 25 void *func1(void *arg)26 {27 printf("t1: %ld\n", pthread_self()); //打印线程ID28 printf("t1: arg = %d\n", *((int *)arg)); //打印传递过来的值29 30 while(1){31 pthread_cond_wait(&cond, &mutex); //等待条件为真32 33 printf("t1 run=========================\n");34 printf("t1: %d\n", g_data);35 g_data = 0;36 37 sleep(1);38 }39 }40 41 void *func2(void *arg)42 {43 printf("t2: %ld\n", pthread_self()); //打印线程ID44 printf("t2: arg = %d\n", *((int *)arg)); //打印传递过来的值45 46 while(1){47 pthread_mutex_lock(&mutex); //加锁48 49 printf("t2: %d\n", g_data++);50 if(g_data == 3){51 pthread_cond_signal(&cond); //当g_data = 3时,触发条件,唤醒等待该条件的线程52 }53 54 pthread_mutex_unlock(&mutex); //解锁55 56 sleep(1);57 }58 }59 60 int main()61 {62 int ret = 0;63 int param = 100;64 65 pthread_t t1;66 pthread_t t2;67 68 //pthread_cond_init(&cond, NULL);69 //pthread_mutex_init(&mutex, NULL);70 71 ret = pthread_create(&t1, NULL, func1, (void *)¶m); //创建线程172 73 if(ret == 0){74 //printf("main: t1:%ld, pthread create success\n", pthread_self());75 }76 77 ret = pthread_create(&t2, NULL, func2, (void *)¶m); //创建线程278 79 if(ret == 0){80 //printf("main: t2:%ld, pthread create success\n", pthread_self());81 }82 83 pthread_join(t1, NULL); //等待线程1退出84 pthread_join(t2, NULL); //等待线程2退出85 86 pthread_cond_destroy(&cond); //销毁条件变量87 pthread_mutex_destroy(&mutex); //销毁互斥锁88 89 return 0;90 }