我在这里运行一些线程安全代码。我正在使用互斥锁来保护一次只需要由 1 个线程运行的代码部分。我遇到的问题是使用此代码有时会得到 2 个 Mutex 对象。顺便说一下,这是一个静态函数。如何确保只创建 1 个互斥对象?
/*static*/ MyClass::GetResource()
{
if (m_mutex == 0)
{
// make a new mutex object
m_mutex = new MyMutex();
}
m_mutex->Lock();
我在这里运行一些线程安全代码。我正在使用互斥锁来保护一次只需要由 1 个线程运行的代码部分。我遇到的问题是使用此代码有时会得到 2 个 Mutex 对象。顺便说一下,这是一个静态函数。如何确保只创建 1 个互斥对象?
/*static*/ MyClass::GetResource()
{
if (m_mutex == 0)
{
// make a new mutex object
m_mutex = new MyMutex();
}
m_mutex->Lock();
只需m_mutex
在外部创建GetResource(),
它就可以调用它 - 这将删除围绕实际创建互斥锁的关键部分。
MyClass::Init()
{
m_mutex = new Mutex;
}
MyClass::GetResource()
{
m_mutex->Lock();
...
m_mutex->Unlock();
}
问题是线程可能在检查 m_mutex 是否为 0 后被中断,但不是在它创建互斥体之前,允许另一个线程运行相同的代码。
不要立即分配给 m_mutex。创建一个新的互斥体,然后进行原子比较交换。
您没有提及您的目标平台,但在 Windows 上:
MyClass::GetResource()
{
if (m_mutex == 0)
{
// make a new mutex object
MyMutex* mutex = new MyMutex();
// Only set if mutex is still NULL.
if (InterlockedCompareExchangePointer(&m_mutex, mutex, 0) != 0)
{
// someone else beat us to it.
delete mutex;
}
}
m_mutex->Lock();
否则,用您的平台提供的任何比较/交换功能替换。
另一种选择是使用一次性初始化支持,它在 Windows Vista 及更高版本上可用,或者如果可以的话,只预先创建互斥锁。
惰性互斥初始化并不适合静态方法;您需要一些保证没有人争先恐后地进行初始化。下面使用编译器为类生成单个静态互斥锁。
/* Header (.hxx) */
class MyClass
{
...
private:
static mutable MyMutex m_mutex; // Declares, "this mutex exists, somewhere."
};
/* Compilation Unit (.cxx) */
MyMutex MyClass::m_mutex; // The aforementioned, "somewhere."
MyClass::GetResource()
{
m_mutex.Lock();
...
m_mutex.Unlock();
}
其他一些解决方案将需要您的程序员同行的额外假设。例如,使用“call init()”方法,您必须确保调用了初始化方法,并且每个人都必须知道这条规则。
为什么还要使用指针?为什么不将指针替换为不需要动态内存管理的实际实例呢?这避免了竞争条件,并且不会对函数的每次调用施加性能影响。
由于它只是为了保护一个特定的代码部分,只需在函数内将其声明为静态即可。
static MyClass::GetResource()
{
static MyMutex mutex;
mutex.Lock();
// ...
mutex.Unlock();
该变量是具有静态存储持续时间的局部变量。标准中明确规定:
允许实现在与允许实现在命名空间范围(3.6.2)中静态初始化具有静态或线程存储持续时间的变量相同的条件下,对具有静态或线程存储持续时间的其他块范围变量执行早期初始化。否则,此类变量在控件第一次通过其声明时被初始化;这样的变量在其初始化完成时被认为已初始化。如果初始化抛出异常退出,说明初始化未完成,下次控件进入声明时会再次尝试。如果在变量初始化时控制同时进入声明,则并发执行将等待初始化完成。
最后一句话是你特别感兴趣的。