HackRF 频谱仪之 Pthread 用法简介(一)
制作一个基于 HackRF 的频谱仪之前,先储备一些关于 Pthread 和 SDL 的基本知识,这些应该都会用于频谱仪的 Demo 中。
Pthread
一开始,我以为可以直接在 hackrf 的 callback 中直接调用 SDL 进行绘图(关于 SDL 的内容在下一篇博文中介绍),但是很快发现这个方式有一个缺陷,即由于 SDL 绘图与 callback 之间存在时间差,因此两个操作很难同步,会导致 SDL 绘图出现卡顿。因此后来考虑通过线程的方式进行控制。下面以自己的理解,通过简单的 C demo 来说明在我们的 Hackrf 频谱仪中会用到的线程操作:
构建一个多线程的 demo
要使用线程,首先需要定一个线程标识(pthread identifier), 接下来我们定义两个线程标识分别是 producer 和 consumer 线程:
static pthread_t produceer_pthread;
static pthread_t consumer_pthread;
接下来,我们创建两个线程中的操作,在 producer 线程中,我们每隔 1s 中进行 20次 连续的对一个变量 a 的 +1 操作,并且打印出来;在 consumer 线程中,我们每隔 1s 对同一个变量 a 进行 -1 操作,并打印出来:
void* Producer_Pthread(){
while(keep_running){
for(int i=0;i<20;i++){
a++;
printf("In Producer Thread, a is: %i\n",a);
}
sleep(1);
}
return 0;
}
void* Consumer_Pthread(){
while(keep_running){
a--;
printf("In Consumer Thread, a is: %i\n",a);
sleep(1);
}
return 0;
}
之后,我们在 main 函数中将两个线程启动, pthread_create 函数第一个参数是我们之前定义的线程标识的地址,第三个参数则是我们的线程函数,第二和第四个参数暂时没有用到。
pthread_create(&produceer_pthread,NULL,Producer_Pthread,NULL);
pthread_create(&consumer_pthread,NULL,Consumer_Pthread,NULL);
最后,我们通过一个简单的 SDL 事件方式(关于 SDL 的内容在下一篇博文中介绍)让主函数保持循环,直到我们关闭 SDL Window
SDL_Init(SDL_INIT_EVERYTHING);
SDL_Window *window = SDL_CreateWindow("Test",SDL_WINDOWPOS_UNDEFINED,SDL_WINDOWPOS_UNDEFINED,400,400,SDL_WINDOW_SHOWN);
SDL_Event event;
while(keep_running){
while(SDL_PollEvent(&event)){
if(event.type == SDL_QUIT){
keep_running = 0;
}
}
}
这样我们来看一下两个进程的输出: 可以看到,由于两个线程都会去修改到变量 a,因此输出中会看见 consumer 线程穿插在了 producer 的线程中,因此 producer 的 20次 连续输出被打断了。
为了避免这种问题的出现,我们在通过线程锁(pthread_mutex) 来解决。
构建一个带线程锁的多线程的 demo
为了使用 pthread_mutex,我们首先创建一个 pthread_mutex 变量,并对其做初始化
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
接下来我们只需要在两个线程中对变量 a 做操作的地方加锁 (pthread_mutex_lock) 就可以了,同时在操作完成后解锁 (pthread_mutex_unlock) 就可以释放给另一线程使用了,修改后的两个线程函数为:
producer 函数:
void* Producer_Pthread(){
while(keep_running){
pthread_mutex_lock(&mutex);
for(int i=0;i<20;i++){
a = a+1;
printf("In Producer Thread, a is: %i\n",a);
}
pthread_mutex_unlock(&mutex);
sleep(1);
}
return 0;
}
consumer 函数:
void* Consumer_Pthread(){
while(keep_running){
pthread_mutex_lock(&mutex);
a=a-1;
printf("In Consumer Thread, a is: %i\n",a);
pthread_mutex_unlock(&mutex);
sleep(1);
}
return 0;
}
我们再看一下输出,可以看到现在完全没有 consumer 线程穿插在 producer 的线程中的情况了,都是在 producer 20次对 a++ 的操作后,才会有 consumer 对 a–。特别要注意的是,printf 其实也是对变量 a 的操作,所以我们的加锁和解锁操作需要包含到 printf 这里。