You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

FFTW3 DLL多线程调用报错求助:访问违规读取内存

问题:FFTW3封装为DLL后,OpenMP多线程调用触发内存访问错误

将封装了FFTW3傅里叶函数的DLL通过OpenMP多线程调用时出现异常:多次调用DLL中的dst(基于DFT实现的离散正弦变换)和IDST函数时,触发access violation reading location错误,错误发生在dst函数的// write x to y代码段。

相关代码

DLL中的dst函数实现

void dst(fftwf_complex* x, size_t n)
{
   fftwf_complex* y = fftwf_alloc_complex(size_t);
   
   fftwf_plan plan = 
   fftwf_plan_dft_1d(size_t,y,y,FFTW_FORWARD,FFTW_ESTIMATE);
   
   // write x to y

   fftwf_execute(plan);

   fftwf_destroy_plan(plan);
   fftwf_cleanup();

   // write y to x again

   fftwf_free(y);
}

外部OpenMP多线程调用代码

double Class::Function1(Class& Inputs, int x, int y, int z)
{
   dllFunction(Inputs.a, Inputs.b, Inputs.c, x, y, z);

   return Inputs.z;
}

void Class::Function2(Class& Inputs)
{
   #pragma omp parallel
   {
      #pragma omp for
      for (int i=0;i<5;i++)
      {
         Inputs.z = Obj.Function1(Inputs,x,y,z);
      }
   }
}

注:因FFTW3原生DST函数处理大数据时速度较慢,故采用DFT实现DST。


问题分析与解决方案

1. 内存分配与Plan创建的致命语法错误

dst函数存在低级参数错误:

  • fftwf_alloc_complex(size_t)和fftwf_plan_dft_1d(size_t,y,y,...)中,错误地将类型名size_t作为参数传入,实际应该使用函数参数n。这会导致分配的内存大小完全不符合需求(传入的是类型占位符的固定值,远小于实际需要的内存),后续写入y时必然触发内存越界,多线程下这种越界冲突会直接引发access violation。

2. fftwf_cleanup()的滥用

fftwf_cleanup()是FFTW的全局清理函数,负责释放FFTW内部的全局资源(如内存池、规划缓存)。绝对不能在每个函数调用中执行该操作:多线程环境下,当一个线程调用fftwf_cleanup()时,其他线程可能正在使用这些全局资源,直接导致资源失效,引发内存访问错误。正确做法是在整个程序退出前调用一次即可。

3. OpenMP线程的共享变量竞争

外部调用代码中,Inputs.z是多线程共享变量,在#pragma omp for循环中直接赋值会导致线程竞争。虽然这不一定是当前access violation的直接原因,但会导致结果不可预期,建议使用线程私有变量存储临时结果,再通过同步机制(如#pragma omp atomic)更新共享变量。

4. FFTW多线程调用的注意事项

FFTW的plan创建和执行本身是线程安全的,但要确保每个线程使用独立的plan和内存缓冲区(你当前每个函数调用创建独立plan和y缓冲区的思路是正确的,只需修正前面的参数错误)。如果需要启用FFTW自身的多线程支持,需在程序初始化时调用fftwf_init_threads(),并在创建plan时指定线程数,但你这里是用OpenMP多线程调用单线程FFTW函数,无需额外设置FFTW的线程选项。

修正后的dst函数示例

void dst(fftwf_complex* x, size_t n)
{
   // 用参数n分配正确大小的内存
   fftwf_complex* y = fftwf_alloc_complex(n);
   
   // 用n作为DFT的长度参数
   fftwf_plan plan = 
   fftwf_plan_dft_1d(n, y, y, FFTW_FORWARD, FFTW_ESTIMATE);
   
   // 正确实现x到y的写入,确保长度匹配n
   for (size_t i = 0; i < n; ++i) {
       y[i][0] = x[i][0];
       y[i][1] = x[i][1];
       // 补充DST实现所需的输入预处理逻辑(如镜像扩展)
   }

   fftwf_execute(plan);

   fftwf_destroy_plan(plan);
   // 移除fftwf_cleanup(),移至程序退出时调用

   // 正确实现y到x的写入,确保长度匹配n
   for (size_t i = 0; i < n; ++i) {
       x[i][0] = y[i][0];
       x[i][1] = y[i][1];
       // 补充DST实现所需的输出后处理逻辑
   }

   fftwf_free(y);
}

外部调用的修正建议

void Class::Function2(Class& Inputs)
{
   #pragma omp parallel
   {
      #pragma omp for
      for (int i=0;i<5;i++)
      {
          // 用线程私有变量存储临时结果
          double temp_z = Obj.Function1(Inputs, x, y, z);
          // 原子操作更新共享变量,避免竞争
          #pragma omp atomic
          Inputs.z = temp_z;
          // 可根据业务逻辑调整同步方式(如累加、覆盖)
      }
   }
}

内容的提问来源于stack exchange,提问作者Hansov

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 20:12:45