带你学习多线程编程
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了带你学习多线程编程相关的知识,希望对你有一定的参考价值。
线程概念定义
线程就是进程内部的执行流,一个进程至少有一个线程,线程拥有自己的私有资源同时也会和进程共享资源。
线程独有的资源
- 线程描述符
- 寄存器
- 线程栈
- errno
- 信号掩码
- 实时调度策略
线程和进程共享的资源
- 全局变量
- 堆
- 代码段
- 文件描述符表
- 进程ID和组ID
- 每种信号的处理方式
-
当前工作目录
线程和进程的区别
- 线程是资源调度的最小单位 ,进程时资源分配的最小单位
- 进程是一次程序运行活动,线程是进程中的一个执行路径
- 进程之间不能共享资源,而线程共享所在进程的地址空间和其它资源。同时线程还有自己的栈和栈指针,程序计数器等寄存器。
- 进程有自己独立的地址空间,而线程没有,线程必须依赖于进程而存在。
线程的创建
#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);
参数:
- thread:返回线程的id
- attr:线程属性,可以手动设置(下文将详细描述线程属性)
- start_routine:线程执行的函数的函数地址
- arg:线程执行函数的参数
返回值:
成功返回0,失败返回错误码。线程的终止
有三种方式终止一个线程。
- 用return返回。
- 用pthread_exit();
#include <pthread.h> void pthread_exit(void *retval);
参数:
retval:保存线程退出码,<font color="#dd00dd">这个指针一定要是全局变量或者堆上开辟的。</font><br/> - 用pthread_cancel()
#include <pthread.h> int pthread_cancel(pthread_t thread);
参数:
thread:结束的线程ID(可以结束任意线程)
返回值:
成功返回0,失败返回错误码。
线程的终止不能用exit(),这是进程的终止方式。
线程的等待与分离
为什么需要线程等待
- 不等待,线程结束后不会自动释放资源。
- 会导致过多线程描述符被占用,无法创建新的线程。
线程等待函数
#include <pthread.h> int pthread_join(pthread_t thread, void **retval);
参数:
thread:等待线程的ID
retval:保存退出状态码
返回值:
成功返回0,失败返回错误码。线程分离
当我们不关心线程的退出状态,只希望线程结束系统会自动清理和释放资源,这时我们就可以使用线程分离。
#include <pthread.h> int pthread_detach(pthread_t thread);
参数:
thread:分离的线程ID
返回值:
成功返回0,失败返回错误码。
线程一旦被pthread_detach()分离,就不能再用pthread_join()获取其状态了,也不能再返回链接状态。线程属性
在前面pthread_create()函数时,就涉及到pthread_attr_t *attr线程属性,我们可以手动设置线程属性。
int pthread_attr_init(pthread_attr_t *attr);//初始化线程属性 int pthread_attr_destroy(pthread_attr_t *attr);//销毁线程属性对象
Detach state = PTHREAD_CREATE_JOINABLE//分离属性 int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);//设置分离属性 int pthread_attr_getdetachstate(pthread_attr_t *attr, int *detachstate);//获得分离属性 Scope = PTHREAD_SCOPE_SYSTEM//抢夺资源范围 Inherit scheduler = PTHREAD_INHERIT_SCHED//是否继承线程调度策略 Scheduling policy = SCHED_OTHER//调度策略 Scheduling priority = 0//调度优先级 Guard size = 4096 bytes//线程栈之间的安全区 Stack address = 0x40196000//自己指定线程栈 Stack size = 0x201000 bytes//栈的大小
线程属性这一块,本文只是简单列出,更深层次的掌握需要大家自行查阅资料。
线程的优缺点
线程的优点
- 创建一个线程的代价要比创建一个进程的代价小得多。
- 与进程之间的切换相比较,线程之间的切换操作系统要做的工作少得多。
- 线程占用的资源比进程少得多。
- 能充分利用 多处理器的可并行数量。
- 在等待慢速I/O操作结束时,程序可执行其他的计算任务。
- 计算密集型应用,多处理器运行,可以将计算分布到多个线程中计算。
- I/O密集型应用,为了提高性能,将I/O操作重叠,线程可以等待不同的I/O操作。
线程的缺点
- 性能损失
- 健壮性降低。
- 缺乏访问控制
- 编程难度提高
- 多线程对GDB支持不好
- 多线程对信号支持不好
线程互斥
互斥量
互斥量
- 定义互斥锁:pthread_mutex_t mutex;
- 初始化:pthread_mutex_init(&mutex,NULL);
- 上锁:pthread_mutex_lock(&mutex);
- 解锁:pthread_mutex_unlock(&mutex);
- 销毁:pthread_mutex_destroy(&mutex);
自旋锁
- 定义自旋锁:pthread_spinlock_t spin;
- 初始化:int pthread_spin_init(pthread_spinlock_t *lock, int pshared);
- 上锁:int pthread_spin_lock(pthread_spinlock_t *lock);
- 解锁:int pthread_spin_unock(pthread_spinlock_t *lock);
- 销毁锁:int pthread_spin_destroy(pthread_spinlock_t *lock);
自旋锁与互斥锁的区别
自旋锁和互斥所的区别:互斥锁是当阻在pthread_mutex_lock时,放弃CPU,好让别人使用CPU。自旋锁阻塞在spin_lock时,不会释放CPU,不断的问CPU可以使用了不
读写锁
- pthread_rwlock_t lock;
- 初始化:int pthread_rwlock_init(pthread_rwlock_t restrict rwlock,const pthread_rwlockattr_t restrict attr);
- 读锁:int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
- 写锁:int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
- 解锁:int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
- 销毁锁:int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
读读共享,读写排他,写写排他,写锁优先.
同步
条件变量
- 定义条件变量:pthread_cond_t cond;
- 初始化条件变量:int pthread_cond_init(pthread_cond_t restrict cond,const pthread_condattr_t restrict attr);
- 等待条件:int pthread_cond_wait(pthread_cond_t restrict cond,pthread_mutex_t restrict mutex);
如果没有在锁环境下使用,互斥量形同虚设
如果在锁环境下,将mutex解锁
wait返回时,将mutex置为原来的状态 - 使条件满足:int pthread_cond_signal(pthread_cond_t *cond);
- 销毁条件变量:int pthread_cond_destroy(pthread_cond_t *cond);
综合案例
pthread_mutex_t mutex;//创建互斥量
int a = 0;
int b = 0;
void *r1(void* arg) //线程1执行函数
{
while(1)
{
pthread_mutex_lock(&mutex);//上锁
a++;
b++;
if(a != b)
{
printf("%d != %d\n",a,b);
}
pthread_mutex_unlock(&mutex);//解锁
}
}
void *r2(void* arg)//线程2执行函数
{
while(1)
{
pthread_mutex_lock(&mutex);
a++;
b++;
if(a != b)
{
printf("%d != %d\n",a,b);
}
pthread_mutex_unlock(&mutex);
}
}
int main(void)
{
pthread_t t1,t2;
pthread_mutex_init(&mutex,NULL);//初始化互斥量
pthread_create(&t1,NULL,r1,NULL);//创建线程
pthread_create(&t2,NULL,r2,NULL);//创建线程
pthread_join(t1,NULL);//线程等待
pthread_join(t2,NULL);
return 0;
}
以上是关于带你学习多线程编程的主要内容,如果未能解决你的问题,请参考以下文章