gpt4 book ai didi

c++ - std::mutex 锁在覆盖新运算符时挂起

转载 作者:塔克拉玛干 更新时间:2023-11-03 00:48:56 34 4
gpt4 key购买 nike

我们有一个内部内存管理器,用于我们的一个产品。内存管理器覆盖了 newdelete 运算符,并且在单线程应用程序中工作良好。但是,我现在的任务是让它也适用于多线程应用程序。根据我的理解,以下伪代码应该可以工作,但即使使用 try_lock(),它也会挂起。有什么想法吗?

更新 #1

导致“访问冲突”:

#include <mutex>

std::mutex g_mutex;

/*!
\brief Overrides the Standard C++ new operator
\param size [in] Number of bytes to allocate
*/
void *operator new(size_t size)
{
g_mutex.lock(); // Access violation exception
...
}

导致线程在自旋中永远挂起:

#include <mutex>

std::mutex g_mutex;
bool g_systemInitiated = false;


/*!
\brief Overrides the Standard C++ new operator
\param size [in] Number of bytes to allocate
*/
void *operator new(size_t size)
{
if (g_systemInitiated == false) return malloc(size);
g_mutex.lock(); // Thread hangs forever here. g_mutex.try_lock() also hangs
...
}

int main(int argc, const char* argv[])
{
// Tell the new() operator that the system has initiated
g_systemInitiated = true;
...
}

更新#2

递归互斥锁还会导致线程在自旋中永远挂起:

#include <mutex>

std::recursive_mutex g_mutex;
bool g_systemInitiated = false;


/*!
\brief Overrides the Standard C++ new operator
\param size [in] Number of bytes to allocate
*/
void *operator new(size_t size)
{
if (g_systemInitiated == false) return malloc(size);
g_mutex.lock(); // Thread hangs forever here. g_mutex.try_lock() also hangs
...
}

int main(int argc, const char* argv[])
{
// Tell the new() operator that the system has initiated
g_systemInitiated = true;
...
}

更新#3

Jonathan Wakely 建议我应该尝试 unique_lock 和/或 lock_guard,但锁仍然挂起。

unique_lock 测试:

#include <mutex>

std::mutex g_mutex;
std::unique_lock<std::mutex> g_lock1(g_mutex, std::defer_lock);
bool g_systemInitiated = false;

/*!
\brief Overrides the Standard C++ new operator
\param size [in] Number of bytes to allocate
*/
void *operator new(size_t size)
{
if (g_systemInitiated == false) return malloc(size);
g_lock1.lock(); // Thread hangs forever here the first time it is called
...
}

int main(int argc, const char* argv[])
{
// Tell the new() operator that the system has initiated
g_systemInitiated = true;
...
}

lock_guard 测试:

#include <mutex>

std::recursive_mutex g_mutex;
bool g_systemInitiated = false;


/*!
\brief Overrides the Standard C++ new operator
\param size [in] Number of bytes to allocate
*/
void *operator new(size_t size)
{
if (g_systemInitiated == false) return malloc(size);
std::lock_guard<std::mutex> g_lock_guard1(g_mutex); // Thread hangs forever here the first time it is called
...
}

int main(int argc, const char* argv[])
{
// Tell the new() operator that the system has initiated
g_systemInitiated = true;
...
}

我认为我的问题是 delete 在锁定时被 C++ 11 互斥库调用。 delete 也像这样被覆盖:

/*!
\brief Overrides the Standard C++ new operator
\param p [in] The pointer to memory to free
*/
void operator delete(void *p)
{
if (g_systemInitiated == false)
{
free(p);
}
else
{
std::lock_guard<std::mutex> g_lock_guard1(g_mutex);
...
}
}

这会导致死锁情况,我看不到任何好的解决方案,除了我自己的锁定不会在锁定或解锁。

更新#4

我已经实现了我自己的自定义递归互斥锁,它没有调用 newdelete,而且,它允许同一个线程进入一个锁定的 block 。

#include <thread>

std::thread::id g_lockedByThread;
bool g_isLocked = false;
bool g_systemInitiated = false;

/*!
\brief Overrides the Standard C++ new operator
\param size [in] Number of bytes to allocate
*/
void *operator new(size_t size)
{
if (g_systemInitiated == false) return malloc(size);

while (g_isLocked && g_lockedByThread != std::this_thread::get_id());
g_isLocked = true; // Atomic operation
g_lockedByThread = std::this_thread::get_id();
...
g_isLocked = false;
}

/*!
\brief Overrides the Standard C++ new operator
\param p [in] The pointer to memory to free
*/
void operator delete(void *p)
{
if (g_systemInitiated == false)
{
free(p);
}
else
{
while (g_isLocked && g_lockedByThread != std::this_thread::get_id());
g_isLocked = true; // Atomic operation
g_lockedByThread = std::this_thread::get_id();
...
g_isLocked = false;
}
}

int main(int argc, const char* argv[])
{
// Tell the new() operator that the system has initiated
g_systemInitiated = true;
...
}

更新#5

尝试了 Jonathan Wakely 的建议,发现微软对 C++ 11 Mutexes 的实现似乎肯定有问题;如果使用 /MTd(多线程调试)编译器标志编译,他的示例将挂起,但如果使用 /MDd(多线程调试 DLL)编译器标志编译,则可以正常工作.正如 Jonathan 正确指出的那样,std::mutex 实现应该是 constexpr 的。这是我用来测试实现问题的 VS 2012 C++ 代码:

#include "stdafx.h"

#include <mutex>
#include <iostream>

bool g_systemInitiated = false;
std::mutex g_mutex;

void *operator new(size_t size)
{
if (g_systemInitiated == false) return malloc(size);
std::lock_guard<std::mutex> lock(g_mutex);
std::cout << "Inside new() critical section" << std::endl;
// <-- Memory manager would be called here, dummy call to malloc() in stead
return malloc(size);
}

void operator delete(void *p)
{
if (g_systemInitiated == false) free(p);
else
{
std::lock_guard<std::mutex> lock(g_mutex);
std::cout << "Inside delete() critical section" << std::endl;
// <-- Memory manager would be called here, dummy call to free() in stead
free(p);
}
}

int _tmain(int argc, _TCHAR* argv[])
{
g_systemInitiated = true;

char *test = new char[100];
std::cout << "Allocated" << std::endl;
delete test;
std::cout << "Deleted" << std::endl;

return 0;
}

更新 #6

向 Microsoft 提交错误报告: https://connect.microsoft.com/VisualStudio/feedback/details/776596/std-mutex-not-a-constexpr-with-mtd-compiler-flag#details

最佳答案

mutex 库使用 new,而 std::mutex 默认是 not 递归的(即可重入的)。鸡生蛋还是蛋生鸡的问题。

更新 正如下面的评论中所指出的,使用 std::recursive_mutex 可能有效。但是,全局变量的静态初始化顺序未明确定义的经典 C++ 问题仍然存在,外部访问全局互斥量的危险也是如此(最好将其放在匿名命名空间中。)

更新 2 您可能过早地将 g_systemInitiated 切换为 true,即在互斥量有机会完成其初始化之前,因此对 的“首次通过”调用malloc() 永远不会发生。要强制执行此操作,您可以尝试将 main() 中的赋值替换为调用分配器模块中的初始化函数:

namespace {
std::recursive_mutex g_mutex;
bool g_initialized = false;
}
void initialize()
{
g_mutex.lock();
g_initialized = true;
g_mutex.unlock();
}

关于c++ - std::mutex 锁在覆盖新运算符时挂起,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14319344/

34 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com