在Android UWB的硬件抽象层的实现中涉及比较多的linux多线程编程相关的内容,本文将相关知识进行简单梳理,以便能够进一步加深相关实现的理解。
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
函数等待创建的线程结束。
线程的执行顺序和调度由操作系统内核决定。
使用线程时需要小心处理共享资源的同步和互斥,以避免并发访问引发的问题。在线程函数中结合上信号量、互斥锁等常见的同步机制即可满足常规的多线程任务需求。
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);
}
可以看到,在写信号之前,都需要将互斥锁加锁,进入临界区执行。
信号量是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
实现线程或进程之间的同步和互斥操作。
在使用信号量时应保证正确的配对和顺序,以避免潜在的并发问题和死锁情况。
pthread_mutex_t
是POSIX线程库中用于互斥锁的数据类型。是线程同步的一种机制,用于保护共享资源的访问,以防止并发访问造成的数据竞争和不一致性。
通过以下函数进行初始化、锁定和解锁:
//锁定与解锁之间构成临界区操作
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;