为了在一定程度上克服多进程的上述缺点,人们引入了线程(Thread)。这是为了将进程的各种缺点降至最低限度(不能直接消除)而设计出的一种“轻量级进程”(Light Weight Process,LWP)。
线程是进程的一个实体,是CPU调度和分派的基本单位。它是比进程更小的能独立运行的基本单位。线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属于一个进程的其他的线程共享进程拥有的全部资源。
每个进程的内存空间都由保存变量的 “数据区”、使用malloc等函数动态分配的堆区(Heap)、函数运行时使用的栈区(Stack)构成,每个进程都有这种独立的内存空间。但如果以获得多个代码执行流为主要目的,只需分离栈区域,通过这种方式可以获得如下优势:
- 上下文切换时不需要切换数据区和堆区。
- 可以利用数据区和堆区交换数据,无需特殊技术
- 线程的创建和上下文切换比进程的创建和上下文切换速度更快。
进程和线程的主要区别在于它们是不同的操作系统资源管理方式。
不能使用perror函数处理错误信息。
处理错误信息函数:strerror(ret)
#include
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);
/*
功能:创建一个新的线程
参数:
pthread_t *thread:以出参的方式获得新创建线程的ID
const pthread_attr_t *attr:用于定制各种不同的线程属性,NULL为创建默认属性的线程
void *(*start_routine) (void *):在单独执行流中执行的函数地址值(函数指针)
void *arg:通过第三个参数传递调用函数时包含传递参数信息的变量地址值
返回值:
成功:0
失败:错误编号
*/
#include
void pthread_exit(void *retval);
/*
功能:线程主动退出函数
参数:
void *retval:出时,需要传递给pthread_join的数据,不传数据用 NULL
返回值:
成功:0
失败:错误编号
*/
//作为保留
int pthread_cancel(pthread_t thread);//线程取消函数
int pthread_detach(pthread_t thread);//分离线程函数
#include
int pthread_join(pthread_t thread, void **retval);
/*
功能:等待一个线程的结束
参数:
pthread_t thread:需要等待的线程ID
void **retval:指向 pthread_exit返回的数据
返回值:
成功:0
失败:错误编号
*/
pthread_t pthread_self (void);
/*
功能:获取当前线程id
参数:
无
返回值:
线程ID 无符号长整型,输出格式为 "%lu"
*/
临界资源: 是指在同一个时刻只允许有限个任务可以访问或修改的资源,通常包括硬件资源(CPU、内存、外设等)和软件资源(共享代码段、共享结构、变量等)
临界区(Critical Section):
在任意时刻只允许一个线程对共享资源进行访问的区域。这个临界区可能是代码块、或是共享内存。
例如,多个线程同时访问同一个全局变量,如果都是读取操作,则不会出现问题。 如果一个线程负责改变此变量的值,而其他线程负责同时读取变量的值,则不能保证读取到的数据是经过写线程修改后的。为了确保读线程读取到的是经过修改后的值,就必须在向全局变量写入数据时禁止其他线程对其的任何访问,直至赋值过程结束后再解除对其他线程的访问限制。
从代码角度讲,全局变量就是共享资源,访问全局变量的语句或语句块就是临界区;从内存角度讲,全局变量所在内存空间中的数据就是共享资源,而内存空间的大小就是临界区。
当一个进程中存在两个及以上(一个进程本来就有一个线程)的线程时,线程间会互相争夺共享资源,导致单个线程中的执行秩序会被打乱。所以需要用到互斥量来进行秩序控制,保证单个线程中的程序先执行完毕。
互斥量也叫互斥锁,本人觉得互斥锁更加的贴切,因为互斥量是锁定共享资源(也可以是一段代码),等共享资源被处理完毕后(执行完一段代码),然后去解锁。而另一个线程必须等到这个锁解开了,才能执行自己的代码。注意互斥锁不能控制多线程的执行顺序,也就是我们加了锁之后,并不知道先执行那个线程,我们只能知道,程序执行到有互斥锁的线程后,会把被锁定的代码先执行完毕。
互斥锁的相关API都放在了
,编译时需要加动态库 -lpthread
pthread_mutex_t mutex;//定义互斥锁的索引
//使用的时候,应该把 mutex定义为全局变量,这样所有的线程都能使用
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);
/*
功能:创建一个互斥锁
参数:
pthread_mutex_t *mutex:互斥量的索引(名称)指针类型
const pthread_mutexattr_t *attr:互斥量的属性,通常为 NULL
返回值:
成功:返回0
失败:返回错误编码
*/
int pthread_mutex_destroy(pthread_mutex_t *mutex);
/*
功能:销毁一个互斥锁,归还自己的一切资源
参数:
pthread_mutex_t *mutex:互斥量的索引(名称)指针类型
返回值:
成功:返回0
失败:返回错误编码
所有线程执行完毕后销毁互斥锁
*/
int pthread_mutex_lock(pthread_mutex_t *mutex);
/*
功能:锁定某个临界区
参数:
pthread_mutex_t *mutex:互斥量的索引(名称)指针类型
返回值:
成功:返回0
失败:返回错误编码
*/
int pthread_mutex_unlock(pthread_mutex_t *mutex);
/*
功能:解锁某个临界区
参数:
pthread_mutex_t *mutex:互斥量的索引(名称)指针类型
返回值:
成功:返回0
失败:返回错误编码
*/
#include
pthread_mutex_t mutex;//定义互斥锁的索引
void *Pthread_T1(void *arg)
{
pthread_mutex_lock(&mutex);
.......//执行一些代码
pthread_mutex_unlock(&mutex);
pthread_exit(NULL);
}
int main()
{
int ret;
pthread_t pthread_T1;
int T1_ret=pthread_create(&pthread_T1,NULL,(void *)Pthread_T1,NULL);//创建一个子线程
ret=pthread_mutex_init(&mutex,NULL);//创建互斥锁
............
pthread_mutex_destroy(&mutex);
return 0;
}
线程的信号量与进程间通信中使用的信号量的概念是一样,它是一种特殊的变量,它可以被增加或减少,但对其的关键访问被保证是原子操作。如果一个程序中有多个线程试图改变一个信号量的值,系统将保证所有的操作都将依次进行。
互斥量与信号量的主要区别:
互斥量用于线程的互斥。
信号量用于线程的同步。
互斥:是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它 性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的。
同步:是指在互斥的基础上(大多数情况),通过其它机制实现访问者对资源的有序访问。在大多数情况下,同步已经实现了互斥,特别是所有写入资源的情况必定是互斥的。少数情况是指可以允许多个访问者同时访问资源。
#include
int sem_init(sem_t *sem,int pshared,unsigned int value);
/*
功能:创建信号量
参数:
sem_t *sem:指向信号量结构的一个指针,创建时应定义为全局变量
int pshared:
0:只能为当前进程的所有线程共享
非0:信号量在进程间共享
unsigned int value:信号量的初始值
返回值:
成功:返回0
失败:返回错误编码
*/
int sem_destroy(sem_t *sem);
/*
功能:销毁信号量,归还自己的一切资源
参数:
sem_t *sem:sem_init函数初始化后的信号量结构体的指针
返回值:
成功:返回0
失败:返回错误编码
*/
int sem_wait(sem_t *sem);
/*
功能:使信号量的值减1,如果预计执行后的结果<0,则阻塞等待
参数:
sem_t *sem:sem_init函数初始化后的信号量结构体的指针
返回值:
成功:返回0
失败:返回错误编码
*/
说明:
int sem_post(sem_t *sem);
/*
功能:使信号量的值加1
参数:
sem_t *sem:sem_init函数初始化后的信号量结构体的指针
返回值:
成功:返回0
失败:返回错误编码
*/
说明:
sem_post函数的作用是给信号量的值加上一个“1”,它是一个“原子操作”,即同时对同一个信号量做加“1”操作的两个线程是不会冲突的;而同时对同一个文件进行读、加和写操作的两个程序就有可能会引起冲突。信号量的值永远会正确地加一个“2”--因为有两个线程试图改变它。
在进程里,如果没有条件信号去控制线程的话,我们无法确定进程会先执行哪个线程,但是使用条件信号的话,我们可以去控制线程的执行顺序。
条件本身是由互斥量保护的。线程在改变条件状态前必须首先锁住互斥量,其他线程在获得互斥量之前不会察觉到这种改变,因为必须锁定互斥量以后才能计算条件。
条件变量使用之前必须首先初始化,pthread_cond_t数据类型代表的条件变量可以用两种方式进行初始化,可以把常量PTHREAD_COND_INITIALIZER赋给静态分配的条件变量。
//使用的时候,应该把 *cond定义为全局变量,这样所有的线程都能使用
int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr);
/*
功能:创建条件信号
参数:
pthread_cond_t *cond:条件信号的索引(名称),是指针变量
const pthread_condattr_t *attr:条件信号的属性,通常为 NULL
返回值:
成功:返回0
失败:返回错误编码
*/
int pthread_cond_destroy(pthread_cond_t *cond);
/*
功能:销毁条件信号,释放占用的资源
参数:
pthread_cond_t *cond:条件信号的索引(名称),使用pthread_cond_init()初始化后的变量
返回值:
成功:返回0
失败:返回错误编码
*/
int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t * mutex);
/*
功能:在指定互斥锁中等待条件信号
参数:
pthread_cond_t *restrict cond:条件信号的索引,需要等待的条件信号
pthread_mutex_t * mutex:在mutex这个互斥锁里等待条件信号
返回值:
成功:返回0
失败:返回错误编码
*/
int pthread_cond_signal(pthread_cond_t *restrict cond,);
/*
功能:发送条件信号
参数:
pthread_cond_t *restrict cond:条件信号的索引,需要发送的条件信号
返回值:
成功:返回0
失败:返回错误编码
*/
利用条件信号,先让T2线程执行,把shared_data加到20,然后T1进程把shared_data减到10:
#include
#include
int shared_data=0;
pthread_mutex_t mutex;
pthread_cond_t cond;//定义条件信号索引
void *Pthread_T1(void *arg)
{
pthread_cond_wait(&cond,&mutex);//在mutex锁里等待信号cond
printf("T1:\n");
while(1){
printf("T1:\n");
shared_data--;
printf("T1:shared_data=%d\n",shared_data);
if(shared_data==10){
pthread_exit(NULL);
}
sleep(1);
}
}
void *Pthread_T2(void *arg)
{
printf("T2:\n");
pthread_mutex_lock(&mutex);
while(1){
printf("T2:\n");
shared_data+=2;
printf("T2:shared_data=%d\n",shared_data);
if(shared_data==20){
pthread_cond_signal(&cond);//发送条件信号
pthread_mutex_unlock(&mutex);
pthread_exit(NULL);
}
sleep(1);
}
}
int main()
{
pthread_t T1;
pthread_t T2;
int ret1=pthread_create(&T1,NULL,(void *)Pthread_T1,NULL);
int ret2=pthread_create(&T2,NULL,(void *)Pthread_T2,NULL);
int mutex_ret=pthread_mutex_init(&mutex,NULL);//创建互斥锁
int cond_ret=pthread_cond_init(&cond,NULL); //创建条件信号
int ret3=pthread_join(T1,NULL);
int ret4=pthread_join(T2,NULL);
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);//销毁条件信号
return 0;
}
高端IT培训 www.hqyj.com
https://blog.csdn.net/SupreV/article/details/78761365
https://blog.csdn.net/u010429831/article/details/122581134
https://blog.csdn.net/qq1140920745/article/details/110007124
https://blog.csdn.net/qq1140920745/article/details/110144556