Linux操作系统软件编程——多线程
什么是线程
线程的定义
是轻量级的进程,可以实现多任务的并发。线程是操作系统任务调度的最小单位
线程的创建
由某个进程创建,且进程创建线程时,会为其分配独立的栈区空间(默认8M)。线程和所在的进程,以及进程中的其他线程,共用进程的堆区、文本区、数据区
线程的调度
宏观并行,微观串行
进程和线程的区别
进程 | 线程 | |
单位 | 操作系统资源分配的最小单位 | 操作系统任务调度的最小单位 |
资源消耗 | 资源消耗开销大,每次创建都需要有0-4G的虚拟内存空间 | 资源消耗开销较小,只需要由所在进程为其开辟默认8M的栈区空间 |
效率 | 由操作系统创建,创建耗时大,跨进程调度慢 | 由进程创建,创建耗时小,跨线程调度快 |
通信 | 进程间不能直接通信,需要使用进程间通信机制(IPC) | 通信简单,可以使用线程共享的区域通信(例如全局变量) |
安全性 | 安全性较高,因为各个进程空间独立 | 安全性较低,一个线程异常,可能影响同一进程中的线程 |
线程相关编程
线程的创建:pthread_read();pthread_self():获取当前线程的ID号
线程的调度:由操作系统调度
线程的凋亡:
1,线程退出:在线程任务函数中使用return结束线程或者调用pthread_exit()结束线程
2,线程回收:pthread_join(tid,NULL)
pthread_create()
功能:创建一个新的线程
参数:
thread:保存线程ID的变量地址
attr:线程属性的对象地址,如果是NULL则按照默认属性创建
void *(*start_routine)(void *):函数指针,指向线程启动后要执行的任务
指针名称:start_routine
指向的对象:函数void *()(void *)
arg:为线程任务函数传递的参数,如果不传参则传NULL
返回值:成功则为0,失败则为!0
需要注意的是,在使用这个函数时,需要在创建或者编译时加上后缀,如图所示
当进程中创建了线程,不要直接退出进程(线程之后就接return),在return之前设立一个死循环或者sleep,否则可能线程还未执行,进程就优先结束,连带着线程也结束
pthread_exit()
功能:退出一个线程任务
参数:向回收的线程传递的参数的地址,如果不传递参数则为NULL
pthread_exit(NULL)等价于return NULL
pthread_join()
功能:阻塞等待回收线程资源空间
参数:要回收的线程ID;用来保存线程退出时传递的参数,NULL表示不接收传递的参数
返回值:成功为0,失败为-1
线程属性
1,分离属性:不需要被其他线程回收的线程,将来会被操作系统回收
2,非分离属性:可以被其他线程回收或者结束的线程,默认属性为非分离属性
线程回收策略
1,分离属性的线程:不需要回收(没有空闲的线程可以帮忙回收)
2,调用pthread_join阻塞回收
int pthread_detach(pthread_t thread)
功能:将线程设置成分离属性的线程
线程间通信
临界资源:多个线程可以同时访问的资源,例如全局变量,共享内存区域
然而,在多个线程在访问临界资源时,会存在资源竞争问题,如下
如果按照代码正常思路,最后输出应该是200000,但是最后却输出199997,这是由于对于全局变量n,其中一个线程调用并进行操作,还未将结果重新赋予时,另一个线程也进行了调用,导致两个线程赋予了同一个值。
事实上,数字越大越容易出现这种情况,但是并不代表每次运行都会出现这种情况,所以为了完全避免这种资源竞争问题,需要用到互斥机制
互斥机制
互斥机制:多个线程访问临界资源时,具有排他性访问的机制,即一次只允许一个线程对该临界资源进行访问
实现互斥机制的步骤
1,创建互斥锁:pthread_mutex_t
2,初始化互斥锁:pthread_mutex_init
3,锁上:int pthread_mutex_lock(pthread_mutex_t *mutex)
4,解锁:int pthread_mutex_unlock(pthread_mutex_t *mutex)
5,销毁锁:int pthread_mutex_destroy(pthread_mutex_t *mutex)
pthread_mutex_init
功能:初始化互斥锁
参数:锁住对象的地址;锁的属性,如果是NULL则为默认属性
返回值:成功则为0,失败则为-1
所以上述的例子可改为