https://baike.baidu.com/item/volatile/10606957?fr=ge_ala
加volatile可以解决一个线程非常频繁访问一个变量,而其他线程又去改变这个变量的值会导致前面的线程因为读错误卡死
注意如果一个线程非常频繁访问一个变量,而其他线程又去改变这个变量的值会导致前面的线程因为读错误卡死
代码如下:
问题的现象是:当在if(uo->recording == true) { 前面加个打印,线程就能正常运行,一开始以为是内存踩踏问题,但是按如下方法排查发现不是
如何初步判断是不是其他地方内存踩踏造成的指针变量变化-CSDN博客
最后加打印并去掉awi_audio2_uac_out_start对uo->recording变量的操作就能正常运行
原因:可以看出uo->recording != true的时候,这个线程一直在访问uo->recording变量,这时如果其他线程来调用awi_audio2_uac_out_start来改变uo->recording变量的,那么任务调度切换回原线程的时候,就会有风险以为访问出错而有卡死
static void* awi_audio2_uac_out_proc(void *arg)
{
awi_audio2_uac_out_t *uo;
awi_alsa_record2_t *ar = NULL;
awi_alsa_record2_params_t *ar_params;
snd_pcm_uframes_t frames;
awi_audio2_uac_out_msg_t *energy_msg;
uint8_t buffer[AWI_AUDIO2_UAC_OUT_RECORD_BUFFER_SIZE];
uo = (awi_audio2_uac_out_t*)arg;
ar_params = &uac_out_params;
ar = awi_alsa_record2_new(ar_params);
if(NULL == ar) {
aw_print("failed to new alsa record");
goto fail;
}
awi_alsa_record2_set_tag(ar, "uac out");
frames = uac_out_params.period_time * uac_out_params.sample_rate / (1000 * 1000);
while(uo->runing) {
int readed = frames;
if(uo->recording == true) {
readed = awi_alsa_record2_read(ar, buffer, frames);
if(readed == frames){
uo->is_play = true;
energy_msg = awi_audio2_uac_out_pop_msg(uo);
if(NULL != energy_msg) {
memcpy(energy_msg->buffer, buffer, AWI_AUDIO2_UAC_OUT_RECORD_BUFFER_SIZE);
awi_blockqueue_push(&uo->msg_energy_q, &energy_msg->q_n);
}
if (uo->data_func != NULL){
uo->data_func(uo->data_ths, buffer, AWI_AUDIO2_UAC_OUT_RECORD_BUFFER_SIZE);
}
}else{
uo->is_play = false;
// #ifdef AUDIO2_DEBUG
aw_print("unexpect readed %d/%ld", readed, frames);
// #endif
if(NULL != ar) {
awi_alsa_record2_del(ar);
}
ar = awi_alsa_record2_new(ar_params);
if(NULL == ar) {
aw_print("failed to new alsa record");
}
awi_alsa_record2_set_tag(ar, "uac out");
}
}
}
fail:
if(NULL != ar) {
awi_alsa_record2_del(ar);
}
return NULL;
}
int awi_audio2_uac_out_start(awi_audio2_uac_out_t *uo)
{
uo->recording = true;
return 0;
}
int awi_audio2_uac_out_stop(awi_audio2_uac_out_t *uo)
{
uo->recording = false;
return 0;
}
解决如下,可以在代码循环里面加阻塞或者睡眠
或者使用最简单的办法,在竞争的变量前面加volatile
阻塞的可以参考如下,睡眠的话不太好
static void* awi_audio2_uac_out_proc(void *arg)
{
awi_queue_node_t *qn;
awi_audio2_uac_out_t *uo;
awi_alsa_record2_t *ar = NULL;
awi_alsa_record2_params_t *ar_params;
snd_pcm_uframes_t frames;
awi_audio2_uac_out_msg_t *energy_msg;
uint8_t buffer[AWI_AUDIO2_UAC_OUT_RECORD_BUFFER_SIZE];
uo = (awi_audio2_uac_out_t*)arg;
frames = uac_out_params.period_time * uac_out_params.sample_rate / (1000 * 1000);
while(uo->runing) {
if(uo->is_connect == true) {
qn = awi_blockqueue_pop(&uo->msg_q, 0);
} else {
qn = awi_blockqueue_pop(&uo->msg_q, -1);
if(NULL == qn) {
continue;
}
}
if(NULL != qn) {
awi_audio2_uac_out_msg_t *msg;
msg = awi_offset(qn, awi_audio2_uac_out_msg_t, q_n);
aw_print("msg event = %d", msg->event);
if(msg->event == AWI_AUDIO2_UAC_OUT_MSG_EVENT_CONNECT) {
uo->is_connect = true;
if(ar == NULL) {
aw_print("uac out connect");
ar_params = &uac_out_params;
ar = awi_alsa_record2_new(ar_params);
if(NULL == ar) {
aw_print("failed to new alsa record");
goto fail;
}
awi_alsa_record2_set_tag(ar, "uac out");
}
} else if(msg->event == AWI_AUDIO2_UAC_OUT_MSG_EVENT_DISCONNECT) {
uo->is_connect = false;
if(ar != NULL) {
aw_print("uac out disconnect");
awi_alsa_record2_del(ar);
ar = NULL;
}
}
if (msg != NULL){
awi_free(msg);
}
continue;
}
if(uo->is_connect == true && ar != NULL) {
int readed = awi_alsa_record2_read(ar, buffer, frames);
energy_msg = awi_audio2_uac_out_pop_msg(uo);
if(NULL != energy_msg) {
memcpy(energy_msg->buffer, buffer, AWI_AUDIO2_UAC_OUT_RECORD_BUFFER_SIZE);
awi_blockqueue_push(&uo->msg_energy_q, &energy_msg->q_n);
}
if(readed == frames) {
uo->is_play = true;
if(uo->recording == true && uo->data_func != NULL) {
uo->data_func(uo->data_ths, buffer, AWI_AUDIO2_UAC_OUT_RECORD_BUFFER_SIZE);
}
} else {
uo->is_play = false;
#ifdef AUDIO2_DEBUG
aw_print("unexpect readed %d/%ld", readed, frames);
#endif
if(NULL != ar) {
awi_alsa_record2_del(ar);
}
ar = awi_alsa_record2_new(ar_params);
if(NULL == ar) {
aw_print("failed to new alsa record");
}
awi_alsa_record2_set_tag(ar, "uac out");
}
}
}
fail:
if(NULL != ar) {
awi_alsa_record2_del(ar);
}
return NULL;
}
还有一点就是线程不要空转,因为空转的时候在运行这个线程的时候就是什么事情不做,那么这个线程的时间片就是浪费了,应该加阻塞或者睡眠,这样子任务调度就不会调这个线程,把cpu给应该用的线程,防止资源的浪费
还需要注意一点:加锁是不能解决这个问题的,不能解决问题是因为下面的加锁有问题,按下文加应该是可以的
使用pthread_cond_wait和pthread_mutex_lock来解决变量竞争的问题-CSDN博客