Linux多线程编程
线程是轻量级的进程,切换成本低,效率高(上下文切换快,启动更廉价),共享进程的的资源所以内存开销小。进程是资源分配的最小单位,线程是操作系统执行调度的最小单位。多线程是指在一个进程里同时运行多个独立的线程。他们共享进程的资源,但有各自的执行路径,能提高程序运行的效率。可通过给不同线程设置优先级,运用于实时操作系统中的实时抢占等地方。在复杂任务时一般线程数量和CPU核心数相同比较合适。注:主线程执行完毕后就会释放内存空间,包括子线程。
#include<pthread.h>
编译:gcc test.c -lpthread -o test
一 . 线程函数
线程创建函数:使用前应先创建一个线程pthread_t 类型的 线程 ID。
pthread_create(pthread_t, *thread, const pthread _attr_t *attr,void *(*start_routine)(void *),void *arg);
//pthread_create(线程ID指针(pthread_t 类型), NULL, 线程任务指针(返回值和参数都是void*类型),线程任务参数)
线程退出函数:让当前线程退出并且不影响其他线程,只有当所有的线程退出后才会回收内存空间。
pthread_exit(void *retval);
//pthread_exit(当前线程退出后所需要保留的的数据的地址,不需要则NULL)
线程回收函数:主线程或其他子线程回收子线程资源(全局变量或处于主线程中的变量由子线程赋值的),每次只能回收一个子线程。
pthread_join(pthread_t, void **retavl);
//pthread_join(线程ID,子线程退出时的参数的地址,可以为NULL)
线程分离函数:主线程分离子线程,线程分离后主线程调用pthread_join也不能回收子线程资源。子线程需要自己释放所有资源。
pthread_detach(pthread_t thread);
//pthread_detach(子线程ID)
线程取消:在线程A中取消(杀死)另外一个线程B,但是必须在线程B中进行一次系统调用(系统函数)才能杀死。
pthread_cancel(pthread_t thread);
//pthread_cancel(需要杀死的线程)
线程比较:
pthread_equal(pthread_t thread1 ,pthread_t thread2);
//pthread_equal(线程一ID,线程二ID),相等返回大于零的数,不相等返回0
二 . 线程同步
由于子线程之间有部分共享资源,当某一时刻多个线程对这段共享资源同时进行读写,可能会造成混乱,所以当有一个线程在对该部分内存进行读写时,需保证其他线程不能进行操作,这就是线程同步。线程同步并不是为了使各个线程能够同时同步对某一个对象进行操作,恰恰相反他是为了让线程“不同步”操作,让线程操作能够依次进行。一般有以下同步方式:互斥锁,读写锁,条件变量,信号量。
2.1 互斥锁
互斥锁的作用是为了保证同一时间内只有一个线程能够访问共享资源,避免多个线程访问导致数据错乱,互斥锁对应某一个共享资源,只能某一个线程锁住(打开),不能被多个线程同时锁住。在临界区前面加锁,临界区结束解锁;临界区:对该共享资源进行操作的部分。使用互斥锁时应当应避免死锁,比如使用共享资源结束后应当解锁,不要重复加锁,因为没有解锁的话,无论是原线程还是其他线程都会无法使用,造成死锁的情况。
pthread_mutex_t mutex;
//创建锁
int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_ *restrict attr);
int pthread_mutex_init(锁的地址,NULL属性)
//初始化锁,*restrict代表即使复制该指针的其余指针也无法访问
int pthread_mutex_destroy(pthread_mutex_t *mutex);
int pthread_mutex_destroy(锁的地址)
//释放互斥锁
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_lock(锁的地址)
//线程调用锁(若锁已被调用则阻塞
int pthread_mutex_unlock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(锁的地址)
//线程解锁
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(锁的地址)
//线程尝试调用锁(若锁已被调用则不会阻塞)
2.2 读写锁
读写锁是互斥锁的升级版,在进行读操作时可以让多个线程同时进行。读写锁分为两种操作——读操作和写操作,如果读写锁锁的是读操作那么就是并行的,如果锁的是写操作那么是串行的,如果是同时锁住读和写,那么写的操作高于读(先写后读)。即可以同时读,但不能同时写(此时与互斥锁一样)。
pthread_rwlock_t rwlock;
//创建读写锁;
int pthread_rwlock_init(phtread_rwlock_t *restrict rwlock, const pthread_rwlock_t *restrict aatr);
int pthread_rwlock_init(读写锁地址,NULL属性)
//初始化读写锁
int pthread_rwlock_destroy(pthread_rwlock_t *restrict rwlock)
pthread_rwlock_destroy(读写锁地址)
//释放读写锁资源
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_rdlock(读写锁地址)
//锁读操作
int pthread_rwlock_wrlock(pthread_rwlock *rwlock);
int pthread_rwlock_wrlock(读写锁地址)
//锁写操作
int pthread_rwlock_trywrlock(pthread_rwlock *rwlock);
int pthread_rwlock_trywrlock(读写锁地址)
//尝试锁写操作
int pthread_rwlock_unlock(pthread_rwlock *rwlock);
int pthread_rwlock_unlock(读写锁地址);
//解锁
2.3 条件变量
条件变量是用于线程的阻塞,与互斥锁不同的是:只有当条件满足时才会进行阻塞,如果不满足条件变量的条件仍然会出现多个变量访问共享资源。条件变量一般与互斥锁一起使用。
pthread_cond_t cond;
//创建条件变量
int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condaddr_t *restrict attr);
int pthread_cond_init(条件变量地址,NULL属性)
//初始化条件变量
int pthread_cond_destroy(pthread_cond_t *cond);
int pthread_cond_destroy(条件变量地址)
//销毁条件变量
int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);
int pthread_cond_wait(条件变量地址,互斥锁地址)
//线程阻塞函数,让线程阻塞等待条件变量(唤醒),同时释放(不在拥有)互斥锁
int pthread_cond_timedwait(pthread_cond_t *restict cond, const pthread_mutex_t *restrict mutex,const struct timespec *restict time)
int pthread_cond_timedwait(条件变量地址,互斥锁地址,时间结构体),其中struct timespec{time_t tv_sec;tmie_tv_nsec}
//阻塞一段时间后不再阻塞
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_signal(条件变量地址);
// 唤醒阻塞在条件变量上的线程, 至少有一个被解除阻塞
int pthread_cond_broadcast(pthread_cond_t *cond);
int pthread_cond_broadcast(条件变量地址);
// 唤醒阻塞在条件变量上的线程, 被阻塞的线程全部解除阻塞
2.4 信号量
信号量与互斥锁不同的是,他可以控制访问资源的线程数量,而互斥锁只能独占共享资源。其本质是一个受保护的整数计数器,当有可用资源时计数加一并唤醒对应操作,当有线程操作资源则计数器减一,受保护的意思是在进行计数的过程中不会出现“多线程同时修改计数器导致计数混乱”。
#include<semaphore.h>
sem_t sem;
//创建信号量
int sem_init(sem_t *sem,int pshared,unsigned int value);
int sem_init(信号量地址,选择进程(非0)还是线程(0),资源数)
//初始化信号量
int sem_wait(sem_t *sem);
int sem_wait(资源数)
//当资源数大于0就不会阻塞,小于0就会,并其线程会占用资源导致资源数减一
int sem_trywait(sem_t *sem);
int sem_trywait(资源数)
//不会阻塞
int sem_post(sem_t *sem);
int sem_post(资源数);
//调用一次加一
int sem_getvalue(sem_t *sem,int *sval);
int sem_getvalue(资源数,记录地址)
//获取资源个数并传入记录地址
openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。
更多推荐


所有评论(0)