【发布时间】:2013-10-03 13:15:30
【问题描述】:
我想编写一个自定义互斥锁,这样每个线程都可以提供一个参数,该参数表示当前线程想要执行的操作的复杂性。如果操作的复杂性很低,其他线程将像自旋锁一样处于循环中,但如果操作的复杂性中等,每个线程将迭代 50 次,然后将按条件变量休眠,如果操作非常复杂,其他线程将直接睡吧。
enum LockOperation
{
LockOperation_Light = -1,
LockOperation_Medium = 50,
LockOperation_Heavy = 1
};
class CustomMutex
{
private:
protected:
std::atomic<int> mFlag;
std::mutex mMutex;
std::condition_variable mCond;
public:
CustomMutex() { std::atomic_init(&mFlag, 0); };
~CustomMutex() {};
void lock(LockOperation pLockOperation = LockOperation_Medium)
{
int lNewLoopCount = static_cast<int>(pLockOperation);
int lLoopCounter = 0;
int lExpected = 0;
int lLoopCount = std::atomic_load_explicit(&mFlag, std::memory_order_relaxed);
while (true)
{
while(std::atomic_load_explicit(&mFlag, std::memory_order_relaxed) != 0 &&
lLoopCounter != lLoopCount)
++lLoopCounter;
std::atomic_compare_exchange_strong_explicit(
&mFlag,
&lExpected,
lNewLoopCount,
std::memory_order_acquire,
std::memory_order_relaxed);
if(lExpected == 0)
{
return;
}
else if(lLoopCounter == lLoopCount)
{
lExpected = 0;
std::unique_lock<std::mutex> lGuard(mMutex);
mCond.wait(lGuard);
}
else
{
lExpected = 0;
continue;
}
}
};
bcInline void UnLock()
{
std::atomic_store_explicit(&mFlag, 0, std::memory_order_relaxed);
std::lock_guard<std::mutex> lGuard(mMutex);
mCond.notify_one();
};
};
现在假设 thread1 锁定了这个互斥体,并且 thread2 等待,因为它的 loopCounter 到达它的末尾并且在锁定条件变量的互斥体之前,thread1 调用条件变量上的通知。现在 thread2 将休眠,直到另一个线程锁定此互斥体,然后对其调用 unlock。
我是多线程新手,我想学习。我知道我的类可能包含错误或完全错误,但是有没有办法纠正这个问题或编写这样一个互斥锁的好算法。
另一个问题:我的原子操作顺序是否正确?
(对不起,如果我的问题有点模棱两可,我的英语不好)
【问题讨论】:
-
自旋锁在用户空间中是个坏主意。在大多数设计良好的应用程序中,互斥争用非常罕见——“无竞争”锁的代码路径通常是高度优化的。将操作分类为
light、medium或heavy,是让我想到priority inversion 问题的原因之一。 -
我必须同意@BrettHale :(
-
@BrettHale 好的,但我认为我的示例中不会出现优先级反转问题。轻、中和重只是互斥锁的所有者告诉等待线程在调用昂贵的系统调用之前它们必须循环多少的标志。我认为轻型和重型操作之间没有优先级争用,并且获取互斥锁仍然是随机方式。为什么你说自旋锁是个坏主意?在像从链表中添加或删除项目这样的“轻操作”中,我们让线程自旋而不是在互斥锁上调用锁将是一个好主意。 (我不明白“自旋锁在用户空间中是个坏主意”)
标签: c++ multithreading c++11 mutex condition-variable