1. Types.h
//implicit_cast<ToType>(expr)
//From type can be inferred; so one argument is ok;
template<typename To, typename From>
inline To implicit_cast(From const &f)
{
return f;
}
template<typename To, typename From>
inline To down_cast(From* f)
{
if (false)
{
implicit_cast<From*, To>(0);
}
#if !defined(NDEBUG) && !defined(GOOGLE_PROTOBUF_NO_RTTI)
assert(f == NULL || dynamic_cast<To>(f) != NULL); // RTTI: debug mode only!
#endif
return static_cast<To>(f);
}
implicit_cast用于向上转换(派生类->基类),可以在编写代码时提醒使用了隐式转换,用于促使自己编写更高质量代码。例如
class B
{
public:
virtual void foo(){}
};
class D : public B
{
public:
void foo(int x){}
};
int main()
{
B* pb;
D* pd = NULL;
//pb = pd;
pb = implicit_cast<B*, D*>(pd);
return 0;
}
上述情况若直接pb=pd,易忽略派生类到基类的隐式转换;
down_cast则是基类转换为派生类的向下转换,需要使用RTTI(运行时检测机制)检测对象指针类型From* 到To* 是可以转换的,或者From*为空指针时。最终实现转换使用static_cast;
2. gcc原子操作
原子性操作:
x++
从内存读取x的值到寄存器中,寄存器+1, 再把新值写入原内存地址;
time | thread1 | thread2 |
---|---|---|
1 | load eax, x | |
2 | load eax, x | |
3 | add eax, 1 | |
4 | add eax, 1 | |
5 | store x, eax | |
6 | store x, eax |
假设x原始的值为1,那么两线程执行后,x的值为2,然而我们想要的结果为3;可以采用锁来进行临界操作达到目的,但会造成性能变差,所以原子操作很有必要(线程安全的)。
gcc提供的原子操作将这三步操作看作作为一步处理,gcc常用的原子操作:
//atomic.h
T getAndAdd(T x)
{
// in gcc >= 4.7: __atomic_fetch_add(&value_, x, __ATOMIC_SEQ_CST)
//原子自增操作
return __sync_fetch_and_add(&value_, x);//返回value增加前的值
}
T get()
{
// in gcc >= 4.7: __atomic_load_n(&value_, __ATOMIC_SEQ_CST)
//原子比较和交换操作(设置)操作
return __sync_val_compare_and_swap(&value_, 0, 0);
}
T getAndSet(T newValue)
{
// in gcc >= 4.7: __atomic_exchange_n(&value_, newValue, __ATOMIC_SEQ_CST)
//原子赋值操作
return __sync_lock_test_and_set(&value_, newValue);
}
注:使用这些原子操作时,编译时需要加 -march=cpu-type cpu-type常设为"native",自动检测本机cpu类型
无锁队列的实现(CAS实现方式)
EnQueue(x)
{
q = new record();
q->value = x;
q->next = NULL;
do{
p = tail;//取链表尾指针快照;因为多线程环境,可能执行后p不指向链表尾节点
}while(CAS(p->next, NULL, q) != true);
//CAS原子操作,比较p->next和NULL;如果相等,p->next = q,返回true
CAS(tail, p, q);//此时tail == p,所以tail = q,更新尾结点
}
若有线程T1,while中的CAS成功,则其他所有随后线程的CAS都会失败,因为此时tail->next == q,即tail不为尾节点;直到T1线程更新tail指针,其他线程可得到新的tail,正常执行。
不过这种方法可能会造成死循环:线程在执行CAS后还未更新指针就停掉或挂掉,其他线程就会进入死循环。改进方法自行百度无锁队列的实现。
3.volatile关键字
确保本条指令不会因为编译器的优化而省略,且要求每次直接读值。简单地说就是防止编译器对代码进行优化。 当使用volatile声明的变量值的时候,系统总是重新从它所在的内存读取数据,而不是使用保存在寄存器中的备份,即使它前面的指令刚从该处读取过数据。
4.muduo库中的编译选项
//my_muduo/CMakelists.txt
set(CXX_FLAGS
-g //生成调试信息
# -DVALGRIND
-DCHECK_PTHREAD_RETURN_VALUE
-D_FILE_OFFSET_BITS=64 //-D 定义宏
-Wall //提示大部分警告
-Wextra //提示一些额外的警告
-Werror //将警告当作错误处理,停止编译
-Wconversion //可能改变的值的隐式转换,警告
-Wno-unused-parameter//未使用的参数,不给出警告
-Wold-style-cast //c风格的转换,警告
-Woverloaded-virtual //若函数声明隐藏了基类的虚函数,警告
-Wpointer-arith //对函数指针或void*指针进行算数操作时,警告
-Wshadow //局部变量遮盖另一个局部变量或全局变量,警告
-Wwrite-strings //规定字符串常量的类型为const char*, 因此将此地址赋值给non-const char*时将产生警告;帮助在编译期发现企图修改字符串常量的代码
-march=native
# -MMD
-std=c++11
-rdynamic
)