linux系统编程之多线程教程
博客推荐:
https://blog.csdn.net/tiandc/article/details/81489308
https://www.cnblogs.com/xiehongfeng100/p/4620852.html#autoid-0-0-0
https://www.cnblogs.com/luoxn28/p/6087649.html
线程概念
线程是操作系统执行的最小单位,进程是程序运行的实例,在一个进程中至少有一个线程,一个线程只能属于一个进程。假如假如cpu是一个工厂的话,进程就相当于车间,线程就相当于车间中的工人。
“进程——资源分配的最小单位,线程——程序执行的最小单位”
同一个进程中的线程共享地址空间,共享全局变量,静态变量等,不共享局部变量等。
线程相对于进程的优势:
- 进程耗费的资源大,一个多进程的程序,需要创建多个进程,需要有多个地址空间,而分配地址空间的操作无疑是耗费资源比较大的,而多线程程序因为共享同一块地址空间,创建的时候不需要分配地址空间,所以耗费的资源比较少。
- 多个进程之间的通信只能通过IPC(进程间通信:管道,消息队列,共享内存,信号,信号量)来实现,而线程之间通信因为共享同一片地址空间的原因,通信方便。
进程相对于线程的优势:
3. 多进程程序比多线程程序健壮,每个进程都有自己的独立的地址空间,由于保护模式的存在,进程退出对于其他进程没有影响,而多线程程序其他进程的运行要依托于控制线程(主线程:当主进程创建线程之后就退化成线程),当主线程退出时,其他线程就会被强行关闭。
线程API
线程还有其他锁(读写锁,自旋锁),可见上方推荐博文。
线程API:
原型:
// 创建线程
#include <pthread.h>
int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(*start_rtn)(void *), void *restrict arg);
// 返回:若成功返回0,否则返回错误编号
//销毁线程
#include <pthread.h>
int pthread_exit(void *rval_ptr);
// 等待线程
#include <pthread.h>
int pthread_join(pthread_t thread, void **rval_ptr);
// 返回:若成功返回0,否则返回错误编号
// 脱离线程
#include <pthread.h>
int pthread_detach(pthread_t thread);
// 返回:若成功返回0,否则返回错误编号
// 获取线程号
#include <pthread.h>
pthread_t pthread_self(void);
// 返回:调用线程的ID
// 比较线程
#include <pthread.h>
int pthread_equal(pthread_t tid1, pthread_t tid2);
// 返回:若相等则返回非0值,否则返回0
线程互斥锁(mutex)API:
原型:
#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr);
int pthread_mutex_destroy(pthread_mutex_t *mutex);
// 两个函数返回值,成功返回0,否则返回错误码
#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
pthread\_mutex\_init用于初始化互斥锁,mutexattr用于指定互斥锁的属性,若为NULL,则表示默认属性。除了用这个函数初始化互斥所外,还可以用如下方式初始化:pthread\_mutex\_t mutex = PTHREAD\_MUTEX\_INITIALIZER。
pthread\_mutex\_destroy用于销毁互斥锁,以释放占用的内核资源,销毁一个已经加锁的互斥锁将导致不可预期的后果。
pthread\_mutex\_lock以原子操作给一个互斥锁加锁。如果目标互斥锁已经被加锁,则pthread\_mutex\_lock则被阻塞,直到该互斥锁占有者把它给解锁。
pthread\_mutex\_trylock和pthread\_mutex\_lock类似,不过它始终立即返回,而不论被操作的互斥锁是否加锁,是pthread\_mutex\_lock的非阻塞版本。当目标互斥锁未被加锁时,pthread\_mutex\_trylock进行加锁操作;否则将返回EBUSY错误码。注意:这里讨论的pthread\_mutex\_lock和pthread\_mutex\_trylock是针对普通锁而言的,对于其他类型的锁,这两个加锁函数会有不同的行为。
pthread\_mutex\_unlock以原子操作方式给一个互斥锁进行解锁操作。如果此时有其他线程正在等待这个互斥锁,则这些线程中的一个将获得它。
互斥锁进入死锁情况:
精彩博文: https://blog.csdn.net/ls5718/article/details/51896159
- 由于互斥锁(两个,一个一般不会)使用不当,例如有AB锁,AB线程都要对AB锁加锁,A线程加锁了其中A锁之后CPU被B线程抢占,B线程又对B锁进行了加锁,此时再执行下一步对A锁进行加锁时就进行不下去了 ,AB线程各对一个锁进行了加锁,导致两个线程都卡在了拿锁的操作
// 代码示例
A线程 :
pthread_mutex_lock(mutexa); // 对A锁进行加锁
sleep(1); // 让出时间片 ,使线程B抢占cpu
pthread_mutex_lock(mutexb);// 对B锁进行加锁
B线程 :
pthread_mutex_lock(mutexb); // 对B锁进行加锁
sleep(1); // 让出时间片 ,使线程A抢占cpu
pthread_mutex_lock(mutexa);// 对A锁进行加锁
\_\_ FUNCTION \_\_ 指代函数名
解决死锁方式:
精彩博文: https://blog.csdn.net/fapengcheng1996/article/details/80172078
- 线程同步
使用条件变量,一个线程等待,当满足某个条件时,由另外一个线程唤醒。