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

使用Mutex仍出现C++并行程序持续数据竞争警告

解决OpenMP+Mutex并行程序中的ThreadSanitizer数据竞争警告

问题概述

使用OpenMP和Mutex实现共享vector同步,但ThreadSanitizer持续报告内存释放环节的数据竞争,警告指向operator delete函数。程序核心逻辑为文件读取+并行计算,问题出在共享vector的内存销毁阶段。

代码实现

#include <fstream>
#include <sstream>
#include <iostream>
#include <vector>
#include <cstring>
#include <cmath>
#include <omp.h>
#include <mutex>

void readTextPointList(int n, int d, const std::string &strFileName, std::vector<std::vector<double>> &result)
{
  std::vector<std::vector<double>> PointList;
  std::string strLine;
  std::ifstream inFile(strFileName);

  if (inFile.is_open())
  {
    int i = 0;
    while (getline(inFile, strLine) && i < n)
    {
      if (strLine.size() != 0)
      {
        std::stringstream sin(strLine);
        std::vector<double> values(d);
        for (int j = 0; j < d; j++)
        {
          double aa;
          sin >> aa;
          values[j] = aa;
        }
        PointList.push_back(values);
        i++;
      }
    }
    inFile.close();

    // Use std::move to transfer ownership
    result = std::move(PointList);
  }
  else
  {
    std::cout << "Error: Unable to open file." << std::endl;
  }
}

std::mutex solveMutex;

void solve(std::vector<std::vector<double>> &data, size_t width, size_t start)
{
  std::vector<double> tuple(width);

  {
    std::lock_guard<std::mutex> lock(solveMutex); // Lock the critical section
    for (size_t i = 0; i < width; ++i)
    {
      tuple[i] = data[start][i];
    }
  }

  // No need to unlock explicitly; std::lock_guard takes care of it when it goes out of scope
}

auto main(int argc, char **argv) -> int
{
  if (argc < 2)
  {
    std::cout << "Usage: DIMENSIONALITY WINDOW" << std::endl;
    return 0;
  }
  size_t width = strtoul(argv[1], nullptr, 10);
  size_t datasize = strtoul(argv[2], nullptr, 10);

  // Read the data into a shared vector
  std::string filename = "mydata.txt";
  std::vector<std::vector<double>> data;

  readTextPointList(datasize, width, filename, data);

  int core_number = 2;

  int step = datasize / core_number + 1;

#pragma omp parallel for schedule(dynamic)
  for (int i = core_number - 1; i >= 0; i--)
  {
    solve(data, width, i * step);
  }

  return 0;
}

编译命令

g++ -Wall -g -Wextra -fsanitize=thread -pedantic -std=c++20 -O3 -m64 -fopenmp main.cpp

ThreadSanitizer警告详情

==================
WARNING: ThreadSanitizer: data race (pid=25822)
  Write of size 8 at 0x7b0800000060 by main thread:
    #0 operator delete(void*, unsigned long) ../../../../src/libsanitizer/tsan/tsan_new_delete.cpp:150 (libtsan.so.0+0x8e878)
    #1 __gnu_cxx::new_allocator<double>::deallocate(double*, unsigned long) /usr/include/c++/11/ext/new_allocator.h:145 (a.out+0x484c)
    #2 std::allocator<double>::deallocate(double*, unsigned long) /usr/include/c++/11/bits/allocator.h:199 (a.out+0x484c)
    #3 std::allocator_traits<std::allocator<double>>::deallocate(std::allocator<double>&, double*, unsigned long) /usr/include/c++/11/bits/alloc_traits.h:496 (a.out+0x484c)
    #4 std::_Vector_base<double, std::allocator<double>>::_M_deallocate(double*, unsigned long) /usr/include/c++/11/bits/stl_vector.h:354 (a.out+0x484c)
    #5 std::_Vector_base<double, std::allocator<double>>::~_Vector_base() /usr/include/c++/11/bits/stl_vector.h:335 (a.out+0x484c)
    #6 std::vector<double, std::allocator<double>>::~vector() /usr/include/c++/11/bits/stl_vector.h:683 (a.out+0x484c)
    #7 void std::destroy_at<std::vector<double, std::allocator<double>>>(std::vector<double, std::allocator<double>>*) /usr/include/c++/11/bits/stl_construct.h:88 (a.out+0x484c)
    #8 void std::_Destroy<std::vector<double, std::allocator<double>>>(std::vector<double, std::allocator<double>>*) /usr/include/c++/11/bits/stl_construct.h:149 (a.out+0x484c)
    #9 void std::_Destroy_aux<false>::__destroy<std::vector<double, std::allocator<double>>*>(std::vector<double, std::allocator<double>>*, std::vector<double, std::allocator<double>>*) /usr/include/c++/11/bits/stl_construct.h:163 (a.out+0x484c)
    #10 void std::_Destroy<std::vector<double, std::allocator<double>>*>(std::vector<double, std::allocator<double>>*, std::vector<double, std::allocator<double>>*) /usr/include/c++/11/bits/stl_construct.h:196 (a.out+0x484c)
    #11 void std::_Destroy<std::vector<double, std::allocator<double>>*, std::vector<double, std::allocator<double>>>(std::vector<double, std::allocator<double>>*, std::vector<double, std::allocator<double>>*, std::allocator<std::vector<double, std::allocator<double>>>&) /usr/include/c++/11/bits/alloc_traits.h:848 (a.out+0x484c)
    #12 std::vector<std::vector<double, std::allocator<double>>, std::allocator<std::vector<double, std::allocator<double>>>>::~vector() /usr/include/c++/11/bits/stl_vector.h:680 (a.out+0x484c)
    #13 main /home/walid/Desktop/minimise/build sanitize/1_________decl.cpp:90 (a.out+0x2c79)

  Previous read of size 8 at 0x7b0800000060 by thread T5 (mutexes: write M11):
    #0 solve(std::vector<std::vector<double, std::allocator<double>>, std::allocator<std::vector<double, std::allocator<double>>>>&, unsigned long, unsigned long) /home/walid/Desktop/minimise/build sanitize/1_________decl.cpp:56 (a.out+0x310b)
    #1 main._omp_fn.0 /home/walid/Desktop/minimise/build sanitize/1_________decl.cpp:86 (a.out+0x329d)
    #2 <null> <null> (libgomp.so.1+0x1dc0d)

  Mutex M11 (0x55bd20bbf160) created at:
    #0 pthread_mutex_lock ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:4240 (libtsan.so.0+0x53908)
    #1 __gthread_mutex_lock /usr/include/x86_64-linux-gnu/c++/11/bits/gthr-default.h:749 (a.out+0x30cf)
    #2 std::mutex::lock() /usr/include/c++/11/bits/std_mutex.h:100 (a.out+0x30cf)
    #3 std::lock_guard<std::mutex>::lock_guard(std::mutex&) /usr/include/c++/11/bits/std_mutex.h:229 (a.out+0x30cf)
    #4 solve(std::vector<std::vector<double, std::allocator<double>>, std::allocator<std::vector<double, std::allocator<double>>>>&, unsigned long, unsigned long) /home/walid/Desktop/minimise/build sanitize/1_________decl.cpp:53 (a.out+0x30cf)
    #5 main._omp_fn.0 /home/walid/Desktop/minimise/build sanitize/1_________decl.cpp:86 (a.out+0x329d)
    #6 <null> <null> (libgomp.so.1+0x1dc0d)

  Thread T5 (tid=25828, running) created by main thread at:
    #0 pthread_create ../../../../src/libsanitizer/tsan/tsan_interceptors_posix.cpp:969 (libtsan.so.0+0x605b8)
    #1 <null> <null> (libgomp.so.1+0x1e25f)
    #2 __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58 (libc.so.6+0x29d8f)

SUMMARY: ThreadSanitizer: data race ../../../../src/libsanitizer/tsan/tsan_new_delete.cpp:150 in operator delete(void*, unsigned long)
==================
==================
WARNING: ThreadSanitizer: data race (pid=25822)
  Write of size 8 at 0x7b3000000000 by main thread:
    #0 operator delete(void*, unsigned long) ../../../../src/libsanitizer/tsan/tsan_new_delete.cpp:150 (libtsan.so.0+0x8e878)
    #1 __gnu_cxx::new_allocator<std::vector<double, std::allocator<double>>>::deallocate(std::vector<double, std::allocator<double>>*, unsigned long) /usr/include/c++/11/ext/new_allocator.h:145 (a.out+0x487a)
    #2 std::allocator<std::vector<double, std::allocator<double>>>::deallocate(std::vector<double, std::allocator<double>>*, unsigned long) /usr/include/c++/11/bits/allocator.h:199 (a.out+0x487a)
    #3 std::allocator_traits<std::allocator<std::vector<double, std::allocator<double>>>>::deallocate(std::allocator<std::vector<double, std::allocator<double>>>&, std::vector<double, std::allocator<double>>*, unsigned long) /usr/include/c++/11/bits/alloc_traits.h:496 (a.out+0x487a)
    #4 std::_Vector_base<std::vector<double, std::allocator<double>>, std::allocator<std::vector<double, std::allocator<double>>>>::_M_deallocate(std::vector<double, std::allocator<double>>*, unsigned long) /usr/include/c++/11/bits/stl_vector.h:354 (a.out+0x487a)
    #5 std::_Vector_base<std::vector<double, std::allocator<double>>, std::allocator<std::vector<double, std::allocator<double>>>>::~_Vector_base() /usr/include/c++/11/bits/stl_vector.h:335 (a.out+0x487a)
    #6 std::vector<std::vector<double, std::allocator<double>>, std::allocator<std::vector<double, std::allocator<double>>>>::~vector() /usr/include/c++/11/bits/stl_vector.h:683 (a.out+0x487a)
    #7 main /home/walid/Desktop/minimise/build sanitize/1_________decl.cpp:90 (a.out+0x2c79)

  Previous read of size 8 at 0x7b3000000000 by thread T5 (mutexes: write M11):
    #0 std::vector<double, std::allocator<double>>::operator[](unsigned long) /usr/include/c++/11/bits/stl_vector.h:1046 (a.out+0x30f6)
    #1 solve(std::vector<std::vector<double, std::allocator<double>>, std::allocator<std::vector<double, std::allocator<double>>>>&, unsigned long, unsigned long) /home/walid/Desktop/minimise/build sanitize/1_________decl.cpp:56 (a.out+0x30f6)
    #2 main._omp_fn.0 /home/walid/Desktop/minimise/build sanitize/1_________decl.cpp:86 (a.out+0x329d)
    #3 <null> <null> (libgomp.so.1+0x1dc0d)

  Mutex M11 (0x55bd20bbf160) created at:
    #0 pthread_mutex_lock ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:4240 (libtsan.so.0+0x53908)
    #1 __gthread_mutex_lock /usr/include/x86_64-linux-gnu/c++/11/bits/gthr-default.h:749 (a.out+0x30cf)
    #2 std::mutex::lock() /usr/include/c++/11/bits/std_mutex.h:100 (a.out+0x30cf)
    #3 std::lock_guard<std::mutex>::lock_guard(std::mutex&) /usr/include/c++/11/bits/std_mutex.h:229 (a.out+0x30cf)
    #4 solve(std::vector<std::vector<double, std::allocator<double>>, std::allocator<std::vector<double, std::allocator<double>>>>&, unsigned long, unsigned long) /home/walid/Desktop/minimise/build sanitize/1_________decl.cpp:53 (a.out+0x30cf)
    #5 main._omp_fn.0 /home/walid/Desktop/minimise/build sanitize/1_________decl.cpp:86 (a.out+0x329d)
    #6 <null> <null> (libgomp.so.1+0x1dc0d)

  Thread T5 (tid=25828, running) created by main thread at:
    #0 pthread_create ../../../../src/libsanitizer/tsan/tsan_interceptors_posix.cpp:969 (libtsan.so.0+0x605b8)
    #1 <null> <null> (libgomp.so.1+0x1e25f)
    #2 __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58 (libc.so.6+0x29d8f)

SUMMARY: ThreadSanitizer: data race ../../../../src/libsanitizer/tsan/tsan_new_delete.cpp:150 in operator delete(void*, unsigned long)
==================
ThreadSanitizer: reported 2 warnings

警告原因

从栈信息可以明确:

  1. 主线程在main函数退出时销毁局部变量data,触发外层vector和内部子vector的内存释放(对应operator delete调用)。
  2. 子线程T5此时仍在执行solve函数,读取data[start][i]的内容,而该内存区域已被主线程标记为待释放,引发读写数据竞争。

额外问题:你对只读共享数据错误添加了互斥锁,虽然锁本身不会直接引发竞争,但不必要的同步会干扰线程调度,加剧优化场景下的生命周期冲突。


解决方案

方案1:移除不必要的互斥锁(最优)

data在并行区域中是只读的,多线程同时读取不会引发竞争,直接删除锁相关代码即可:

void solve(std::vector<std::vector<double>> &data, size_t width, size_t start)
{
  std::vector<double> tuple(width);
  // 直接读取只读数据,无需锁
  for (size_t i = 0; i < width; ++i)
  {
    tuple[i] = data[start][i];
  }
}

方案2:显式确保线程同步

若怀疑OpenMP隐式屏障失效,可添加显式屏障或手动管理内存生命周期:

#pragma omp parallel for schedule(dynamic)
for (int i = core_number - 1; i >= 0; i--)
{
  solve(data, width, i * step);
}
// 显式屏障,强制等待所有子线程完成
#pragma omp barrier

// 或者用智能指针手动控制内存释放时机
auto data_ptr = std::make_unique<std::vector<std::vector<double>>>();
readTextPointList(datasize, width, filename, *data
相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 14:36:11