我有一个混合锁定类,它旋转尝试锁定一个(编译时固定的)旋转次数,然后再回到std::mutex
上的阻塞,直到锁定变为可用。
简化为:
#include <mutex>
template<unsigned SPIN_LIMIT>
class hybrid_lock {
public:
void lock(){
for(unsigned i(0);i<SPIN_LIMIT;++i){
if(this->mMutex.try_lock()){
return;
}
}
this->mMutex.lock();
}
void unlock(){
this->mMutex.unlock();
}
private:
std::mutex mMutex;
};
在SPIN_LIMIT==0
的特殊情况下,这又回归为“普通”std::mutex
(即没有可见旋转)。
所以我专注于:
template<>
class hybrid_lock<0> : public std::mutex {};
它工作得很好但是批准的专门化类模板的方法是另一个(预先存在的)模板吗?
答案 0 :(得分:2)
注意:我回答了实际问题,而不是标题中的问题。
好吧,现在hybird_lock<0>
和hybird_lock<1>
完全不同,一个派生自std::mutex
,另一个派生/包装它。这改变了hybird_lock
的整体构成及其背后的意义。即它们在语义上并不相同。这可能会导致一些意想不到的后果 - hybird_lock<0>
会继承很多东西,而其他情况则不会。
如果这是唯一的区别,我根本不会打扰专业化。请记住,在编译时将知道零情况,并且当它完成时,整个循环将被完全优化。
如果还有其他重要(或实际)优化,我会选择以下内容:
template<>
class hybrid_lock<0> {
public:
void lock(){
this->mMutex.lock();
}
void unlock(){
this->mMutex.unlock();
}
private:
std::mutex mMutex;
};
这种实现使0
成为一种特殊情况,而不是几乎完全不同的东西。
答案 1 :(得分:1)
没有“官方”的方法,但这是一个好方法 - 使用模板,将主模板类分解为更小的“动作”或“功能”模板类通常更好。通过这种方式,您可以获得更多的控制和粒度而不是专业化,这意味着您只需要在一个地方维护主逻辑:
#include <iostream>
#include <mutex>
// general form of the spin_locker
template<unsigned SPIN_LIMIT, class Mutex>
struct spinner
{
static void lock(Mutex& m) {
for (unsigned i = 0 ; i < SPIN_LIMIT ; ++i)
if (m.try_lock())
return;
m.lock();
}
};
// optmised partial specialisation for zero spins
template<class Mutex>
struct spinner<0, Mutex>
{
static void lock(Mutex& m) {
m.lock();
}
};
template<unsigned SPIN_LIMIT, class Mutex = std::mutex>
class hybrid_lock {
using spinner_type = spinner<SPIN_LIMIT, Mutex>;
public:
void lock(){
spinner_type::lock(mMutex);
}
void unlock(){
mMutex.unlock();
}
std::unique_lock<Mutex> make_lock() {
return std::unique_lock<Mutex>(mMutex);
}
private:
Mutex mMutex;
};
// since only the 'spinner' functor object needs specialising there is now no need to specialise the main logic
using namespace std;
auto main() -> int
{
hybrid_lock<100> m1;
hybrid_lock<0> m2;
hybrid_lock<100, std::recursive_mutex> m3;
hybrid_lock<0, std::recursive_mutex> m4;
auto l1 = m1.make_lock();
auto l2 = m2.make_lock();
auto l3 = m3.make_lock();
auto l4 = m4.make_lock();
return 0;
}
答案 2 :(得分:1)
理查德·霍奇斯的答案很棒,但你可以简单地重载方法lock
:
#include <iostream>
#include <type_traits>
#include <mutex>
template<class Mutex = std::mutex>
struct hybrid_lock {
template<int N>
void lock(std::integral_constant<int, N> val){
for (unsigned i = 0 ; i < val() ; ++i)
if (mMutex.try_lock())
return;
mMutex.lock();
}
void lock(std::integral_constant<int, 0>){
mMutex.lock();
}
void unlock(){
mMutex.unlock();
}
std::unique_lock<Mutex> make_lock() {
return std::unique_lock<Mutex>(mMutex);
}
private:
Mutex mMutex;
};
template <int N>
constexpr
std::integral_constant<int, N> IC;
int main() {
hybrid_lock<> m1;
hybrid_lock<> m2;
m1.lock(IC<0>);
m2.lock(IC<100>);
return 0;
}
IC是variable template,来自c ++ 14,如果你的编译器不支持它,你可以使用类型别名type alias代替:
template<int N>
using IC = std::integral_constant<int, N>;
并像这样使用
m.lock(IC<0>{});