我在这里运行一些线程安全的代码。我正在使用互斥锁来保护需要一次只运行1个线程的代码段。我遇到的问题是使用此代码有时我最终得到2个Mutex对象。顺便说一下,这是一个静态功能。如何确保只创建1个互斥对象?
/*static*/ MyClass::GetResource()
{
if (m_mutex == 0)
{
// make a new mutex object
m_mutex = new MyMutex();
}
m_mutex->Lock();
答案 0 :(得分:12)
只需在m_mutex
之外创建GetResource(),
,然后才能调用它 - 这将删除实际创建互斥锁的关键部分。
MyClass::Init()
{
m_mutex = new Mutex;
}
MyClass::GetResource()
{
m_mutex->Lock();
...
m_mutex->Unlock();
}
答案 1 :(得分:8)
问题是在检查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();
否则,请替换平台提供的任何比较/交换功能。
另一种选择是使用one-time initialization支持,可以在Windows Vista及更高版本上使用,或者只是预先创建互斥锁。
答案 2 :(得分:3)
懒惰的互斥初始化不适合静态方法;你需要保证没有人参加初始化。以下使用编译器为类生成单个静态互斥锁。
/* 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()”方法,您必须确定调用初始化方法,并且每个人都必须知道此规则。
答案 3 :(得分:2)
为什么还要使用指针?为什么不用不需要动态内存管理的实际实例替换指针?这样可以避免竞争条件,并且不会对每次调用函数产生性能影响。
答案 4 :(得分:0)
因为它只是为了保护一个特定的代码段,所以只需在函数内声明它是静态的。
static MyClass::GetResource()
{
static MyMutex mutex;
mutex.Lock();
// ...
mutex.Unlock();
变量是具有静态存储持续时间的局部变量。标准明确规定:
允许实现使用static或者执行其他块范围变量的早期初始化 在允许实现静态初始化的相同条件下的线程存储持续时间 在命名空间范围内具有静态或线程存储持续时间的变量(3.6.2)。否则这样的变量是 初始化第一次控制通过其声明;这样的变量被认为是初始化的 完成初始化。如果初始化通过抛出异常退出,则初始化 未完成,因此下次控制进入声明时将再次尝试。如果控制进入 在初始化变量的同时声明,并发执行应该等待 完成初始化。
最后一句话对您特别感兴趣。