Linux编程——多线程
在Android UWB的硬件抽象层的实现中涉及比较多的linux多线程编程相关的内容,本文将相关知识进行简单梳理,以便能够进一步加深相关实现的理解。
1. 线程(pthread)
POSIX线程(pthread),是一种可移植的多线程标准。
Linux内核支持多种线程调度策略,如抢占式调度和时间片轮转调度,可以通过pthread_attr_t
结构体来进行配置。
Linux中通过内核线程实现和用户空间线程库的结合,可以方便创建、管理和同步多线程应用程序。
以下为Linux线程的简单示例:
#include int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg); #include #include void *thread_function(void *arg) { int *value = (int *)arg; printf("Thread value: %d\n", *value); // 其他操作... return NULL; } int main() { pthread_t thread; int value = 42; // 1. 主任务中创建新线程,线程立即执行 if (pthread_create(&thread, NULL, thread_function, (void *)&value) != 0) { perror("pthread_create"); return 1; } // 其他操作... // 2. 等待新线程结束 if (pthread_join(thread, NULL) != 0) { perror("pthread_join"); return 1; } return 0; }
pthread_create
用于创建一个新线程,线程将在函数返回后立即开始执行。
pthread_join
函数等待创建的线程结束。
线程的执行顺序和调度由操作系统内核决定。
使用线程时需要小心处理共享资源的同步和互斥,以避免并发访问引发的问题。在线程函数中结合上信号量、互斥锁等常见的同步机制即可满足常规的多线程任务需求。
1.1 pthread_cond_***
pthread_cond_timedwait
函数是POSIX线程库中用于条件变量的等待函数之一,它允许线程在指定的时间段内等待条件变量满足特定条件,线程继续执行。
pthread_cond_signal
函数是POSIX线程库中用于条件变量的型号函数,用于唤醒正在等待该条件变量的一个线程。
这两个函数的定义如下:
#include // 互斥锁指针、abstime等待的绝对时间点 int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime); int pthread_cond_signal(pthread_cond_t *cond);
pthread_cond_timedwait
,若mutex未锁定,则返回错误;若已经锁定,函数将释放mutex,并将线程置于等待状态,直到条件变量被唤醒或设置的绝对时间abstime到达。当线程被唤醒时,将重新获取mutex并继续执行。
注意,在使用pthread_cond_timedwait
函数时,需要保证互斥锁已被锁定,并在等待之前解锁。等待过程中,互斥锁会被自动重新锁定。
关于pthread_cond_signal
,如果有线程正在等待该条件变量,将选择一个等待线程并将其唤醒;若没有线程等待该变量,则不会产生任何效果。
在Android UWB的实现中,关于phTmlUwb
中,使用了线程等待函数来实现写等待操作的实现:
// 等待写操作完成 static void phTmlUwb_WaitWriteComplete(void) { int ret; struct timespec absTimeout; clock_gettime(CLOCK_MONOTONIC, &absTimeout); absTimeout.tv_sec += 1; /*1 second timeout*/ gpphTmlUwb_Context->wait_busy_flag = true; //wait_busy_condition,wait_busy_lock互斥锁,等待写完成信号 ret = pthread_cond_timedwait(&gpphTmlUwb_Context->wait_busy_condition, &gpphTmlUwb_Context->wait_busy_lock, &absTimeout); } // 写完成信号 static void phTmlUwb_SignalWriteComplete(void) { int ret; if (gpphTmlUwb_Context->wait_busy_flag == true) { gpphTmlUwb_Context->wait_busy_flag = false; ret = pthread_cond_signal(&gpphTmlUwb_Context->wait_busy_condition); } } // 关于信号量写完成调用 static void* phTmlUwb_TmlWriterThread(void* pParam) { //... /* TML reader writer callback synchronization mutex lock --- START */ pthread_mutex_lock(&gpphTmlUwb_Context->wait_busy_lock); gpphTmlUwb_Context->gWriterCbflag = true; phTmlUwb_SignalWriteComplete(); /* TML reader writer callback synchronization mutex lock --- END */ pthread_mutex_unlock(&gpphTmlUwb_Context->wait_busy_lock); }
可以看到,在写信号之前,都需要将互斥锁加锁,进入临界区执行。
2. 信号量
信号量是Linux多线程编程中使用较多的一种同步工具,以便在多线程/多进程之间进行同步和互斥操作。
#include #include // pshared 是否为线程间共享,为0表示是线程间共享的,可在同一进程的不同线程之间使用;非0表示是进程间共享的,可以在不同进程之间使用。 int sem_init(sem_t *sem, int pshared, unsigned int value); int sem_post(sem_t *sem); int sem_wait(sem_t *sem); int main() { sem_t mySemaphore; // 初始化信号量,初始值为1 if (sem_init(&mySemaphore, 0, 1) == -1) { perror("sem_init"); return 1; } // 其他操作... // 增加信号量的值 sem_post(&mySemaphore); // 销毁信号量 sem_destroy(&mySemaphore); return 0; }
sem_init只能用于未初始化的信号量,若是已经初始化的信号量,应先调用sem_destroy销毁之后再调用sem_init进行初始化。
sem_wait
函数调用,若信号量的值大于0,则将被减1,继续执行;若信号量为0,则线程将被阻塞,直到其他线程或进程调用sem_post函数增加信号量的值。
进而通过sem_wait/sem_post
实现线程或进程之间的同步和互斥操作。
在使用信号量时应保证正确的配对和顺序,以避免潜在的并发问题和死锁情况。
3. 互斥锁
pthread_mutex_t
是POSIX线程库中用于互斥锁的数据类型。是线程同步的一种机制,用于保护共享资源的访问,以防止并发访问造成的数据竞争和不一致性。
通过以下函数进行初始化、锁定和解锁:
- pthread_mutex_init,用于初始化互斥锁
- pthread_mutex_destroy,用于销毁互斥锁
- pthread_mutex_lock,用于锁定互斥锁,阻塞其他线程对互斥锁的访问,直到互斥锁解锁
- pthread_mutex_unlock,用于解锁互斥锁,允许其他线程对互斥锁进行访问。
//锁定与解锁之间构成临界区操作 pthread_mutex_lock(&mutex); // 临界区操作... pthread_mutex_unlock(&mutex);
在UCI HAL实现中,我们可以看到许多与多线程实现相关的变量的定义,以UCI控制结构体为例:
/* UCI Control structure */ typedef struct phNxpUciHal_Control { pthread_t client_thread; /* Integration thread handle */ //... /* Waiting semaphore */ phNxpUciHal_Sem_t ext_cb_data; phNxpUciHal_Sem_t dev_status_ntf_wait; phNxpUciHal_Sem_t uwb_binding_status_ntf_wait; } phNxpUciHal_Control_t; // 其中对信号量进行了一定的封装 /* Semaphore handling structure */ typedef struct phNxpUciHal_Sem { /* Semaphore used to wait for callback */ sem_t sem; /* Used to store the status sent by the callback */ tHAL_UWB_STATUS status; /* Used to provide a local context to the callback */ void* pContext; } phNxpUciHal_Sem_t;