🚙

💨 💨 💨

×

  • Categories

  • Archives

  • Tags

  • About

python中的__name__和__main()__

Posted on 02-10-2015 | In Misc
#hello.py
def sayHello():
str="hello"
print(str);

if __name__ == "__main__":
print ('This is main of module "hello.py"')
sayHello()

python作为一种脚本语言,我们用python写的各个module都可以包含以上那么一个累死c中的main函数,只不过python中的这种__main__与c中有一些区别,类似于php的魔术那一套, 主要体现在:

1、当单独执行该module时,比如单独执行以上hello.py: python hello.py,则输出

This is main of module "hello.py"
hello

可以理解为"if __name__=="__main__":"这一句与c中的main()函数所表述的是一致的,即作为入口;

2、当该module被其它module 引入使用时,其中的"if __name__=="__main__":"

所表示的Block不会被执行,

这是因为此时module被其它module引用时,

其__name__的值将发生变化,__name__的值将会是module的名字。

比如在python shell中import hello后,查看hello.__name__:

import hello
hello.__name__
'hello'

3、因此,在python中,当一个module作为整体被执行时,moduel.name的值将是"__main__";

而当一个module被其它module引用时,module.__name__将是module自己的名字,

当然一个module被其它module引用时,其本身并不需要一个可执行的入口main了。

条件编译指令之#if和#ifdef和#if defined之间的区别

Posted on 02-09-2015 | In Misc

#if的使用说明

#if的后面接的是表达式 :

#if (MAX==10)||(MAX==20)
code...
#endif

它的作用是:如果(MAX==10)||(MAX==20)成立,那么编译器就会把其中的#if 与 #endif之间的代码编译进去(注意:是编译进去,不是执行!!)

#if defined的使用

#if后面接的是一个宏, 而#if define(x)的使用如下 :

#if defined (x)
...code...
#endif

这个#if defined它不管里面的“x”的逻辑是“真”还是“假”它只管这个程序的前面的宏定义里面有没有定义“x”这个宏,如果定义了x这个宏,那么,编译器会编译中间的…code…否则不直接忽视中间的…code…代码。
另外 #if defined(x)也可以取反,也就用 #if !defined(x)

#ifdef的使用

  • #ifdef的使用和#if defined()的用法一致
  • #ifndef又和#if !defined()的用法一致。

最后强调两点:

  • 这几个宏定义只是决定代码块是否被编译!
  • 别忘了#endif

Lua特别之处笔记

Posted on 02-03-2015 | In Misc

2.2 Booleans

两个取值 false 和 true。但要注意 Lua 中所有的值都可以作为条件。在控制结构的条
件中除了 false 和 nil 为假,其他值都为真。所以 Lua 认为 0 和空串都是真。

2.5 table

我们用一个疑问来引入table的介绍

传的是值还是引用?

lua的函数调用传的是值还是引用?

. . .

GDB基础教程

Posted on 02-02-2015 | In Linux

GDB 操作提示

在编译可执行文件时需要给 gcc 加上 “-g” 选项,这样它才会为生成的可执行文件加入额外的调试信息。
不要使用编译器的优化选项,比如: “-O”,”-O2”。因为编译器会为了优化而改变程序流程,那样不利于调试。
在 GDB 中执行 shell 命令可以使用:shell command
GDB 命令可以使用 TAB 键来补全。按两次 TAB 键可以看到所有可能的匹配。
GDB 命令缩写:例如 info bre 中的 bre 相当于 breakpoints。

启动GDB

  • gdb executable
  • gdb -e executable -c core-file
  • gdb executable -pid process-id (使用ps相关命令可以查看进程的 pid)

GDB常用命令

  • help 列出 gdb 帮助信息。
  • info+subcommand , 比如 :
    • info breakpoints : 列出断点。
    • info watchpoints : 列出观察点。
    • info threads : 列出当前的线程。
    • info locals : 列出Local variables of current stack frame
  • step(简写一个s也可) 进入下一行代码的执行,会进入函数内部。
  • next(简写一个n也可) 执行下一行代码。但不会进入函数内部。
  • finish 跳出当前代码(之前跳入调试)
  • continue(c) 继续执行直到下一个断点或观察点。
  • b 断点
  • kill 停止程序执行。
  • quit(q) 退出 GDB调试器
  • run(r) 从头开始执行程序,也允许进行重定向。
  • print(p) variable 打印指定变量的值。
    • p variable
    • p file::variable
    • p ‘file’::variables
  • backtrace(简写bt), 显示函数调用栈
    • bt : 显示所有函数调用栈
    • bt n : 显示程序的调用栈信息,只显示栈顶n桢(frame)
    • bt -n : 显示程序的调用栈信息,只显示栈底部n桢(frame)
    • set backtrace limit n : 设置bt显示的最大桢层数
    • where 和 info stack : 都是bt的别名,功能一样

细说断点

给 test.c 的第10行设置一个断点 :

b test.c:10 

断点删除

断点的删除与断点的设置同样的重要。删除断点的命令有两个:

  • delete
  • clear

delete

用法:delete [breakpoints num] [range…]
delete可删除单个断点,也可删除一个断点的集合,这个集合用连续的断点号来描述。
例如:

delete 5
delete 1-10

clear

用法:

  • clear 删除所选定的环境中所有的断点
  • clear location location描述具体的断点。

clear 删除断点是基于行的,不是把所有的断点都删除。
例如:

clear list_insert         //删除函数的所有断点
clear list.c:list_delet   //删除文件:函数的所有断点
clear 12                  //删除行号的所有断点
clear list.c:12           //删除文件:行号的所有断点

断点的使能和禁止

对断点的控制除了建立和删除外,还可以通过使能和禁止来控制,后一种方法更灵活。

断点的四种使能操作:

  • enable [breakpoints] [range…] 完全使能
  • enable //激活所有断点
  • enable 4 //激活4断点
  • enable 5-6 //激活5~6断点
  • disable [breakpoints] [range…] 禁止
  • enable once [breakpoints] [range…] 使能一次,触发后禁止
  • enable delete [breakpoints] [range…]使能一次,触发后删除

用法举例:

diable                //禁止所有断点
disble 2            //禁止第二个断点
disable 1-5            //禁止第1到第5个断点

GDB帮助

GDB的命令很多, 有些用得少的命令记不住的话, 可以在进入GDB之后敲 “help”, 然后再敲 “help + command_class”,
比如 :

(gdb) help
List of classes of commands:

aliases – Aliases of other commands
breakpoints – Making program stop at certain points
data – Examining data
files – Specifying and examining files
internals – Maintenance commands
obscure – Obscure features
running – Running the program
stack – Examining the stack
status – Status inquiries
support – Support facilities
tracepoints – Tracing of program execution without stopping the program
user-defined – User-defined commands

Type “help” followed by a class name for a list of commands in that class.
Type “help all” for the list of all commands.
Type “help” followed by command name for full documentation.
Type “apropos word” to search for commands related to “word”.
Command name abbreviations are allowed if unambiguous.

(gdb) help running
Running the program.

List of commands:

advance – Continue the program up to the given location (same form as args for break command)
attach – Attach to a process or file outside of GDB
continue – Continue program being debugged
detach – Detach a process or file previously attached
detach checkpoint – Detach from a checkpoint (experimental)
detach inferiors – Detach from inferior ID (or list of IDS)
disconnect – Disconnect from a target
finish – Execute until selected stack frame returns
handle – Specify how to handle signals
inferior – Use this command to switch between inferiors
interrupt – Interrupt the execution of the debugged program
jump – Continue program being debugged at specified line or address
kill – Kill execution of program being debugged
kill inferiors – Kill inferior ID (or list of IDs)
next – Step program
nexti – Step one instruction
reverse-continue – Continue program being debugged but run it in reverse
reverse-finish – Execute backward until just before selected stack frame is called
reverse-next – Step program backward
reverse-nexti – Step backward one instruction
reverse-step – Step program backward until it reaches the beginning of another source line
reverse-stepi – Step backward exactly one instruction
run – Start debugged program
…

单例模式的析构问题和线程安全问题

Posted on 02-02-2015 | In Misc

在某些应用环境下面,一个类只允许有一个实例,这就是著名的单例模式。
单例模式分为

  • 懒汉模式
  • 饿汉模式

饿汉模式

在实例化 m_instance 变量时,直接调用类的构造函数。顾名思义,在还未使用变量时,已经对 m_instance 进行赋值,就像很饥饿的感觉。
在main开始前就初始化好了, 所以是线程安全的。

没有考虑析构问题饿汉模式的示例代码

首先给出没有考虑析构问题的饿汉模式的实现

#include <iostream>

using namespace std;


class singleton
{
protected:
singleton() {};
private:
singleton(const singleton&) {};
singleton& operator=(const singleton&) {};
static singleton* m_instance;
public:
static singleton* GetInstance();
~singleton()
{
printf("Singleton destruction\n");
}
};


singleton* singleton::GetInstance()
{
return m_instance;
}


singleton* singleton::m_instance = new singleton;

int main()
{
singleton *ct = singleton::GetInstance();
return 0;
}

饿汉模式的优点

  • 线程安全
  • 实现简单,容易维护

饿汉模式的缺点

  • 不适合部分场景。如:因为性能问题,希望懒加载;需要运行时才能知道,是否生成实例
  • 由于在main开始前就必须初始化,几乎不可能给类传入任何参数。

懒汉模式

懒汉模式下,在定义m_instance变量时先等于NULL,在调用GetInstance()方法时,在判断是否要赋值。这种模式,并非是线程安全的,因为多个线程同时调用GetInstance()方法,就可能导致有产生多个实例。

没有考虑线程安全与析构问题的懒汉模式的示例代码

下面是没有考虑线程安全以及析构问题的懒汉模式的代码实现

#include <iostream>

using namespace std;

class singleton
{
protected:
singleton() {};
private:
singleton(const singleton&) {};
singleton& operator=(const singleton&) {};
static singleton* m_instance;
public:
static singleton* GetInstance();
~singleton()
{
printf("Singleton destruction\n");
}
};

singleton* singleton::GetInstance()
{
if (m_instance == NULL)
{
m_instance = new singleton;
}
return m_instance;
}

singleton* singleton::m_instance = NULL;

int main()
{
singleton *ct = singleton::GetInstance();
return 0;
}

解决懒汉模式线程安全问题的几种方法

有下面几种方法 :

  • 使用局部静态变量
  • 加锁
  • pthread_once
  • DCL

使用局部静态变量

使用局部静态变量。
局部静态变量的初始化是线程安全的,这一点由编译器保证.(http://gcc.gnu.org/ml/gcc-patches/2004-09/msg00265.html,这是一个 GCC 的 patch,专门解决这个问题)。会在程序退出的时候自动销毁。
见此处

这个方法适合 C++11,C++11保证静态局部变量的初始化是线程安全的。
如果是 C++98 就不能用这个方法。

非摸板版本
class S
{
public:
static S& getInstance()
{
static S instance;
return instance;
}
private:
S() {}
S(S const&); // Don't Implement.
void operator=(S const&); // Don't implement
};

或者

模板版本
template<typename T>
class Singleton
{
public:
static T& GetInstance()
{
static T instance;
return instance;
}

Singleton(T&&) = delete;
Singleton(const T&) = delete;
void operator= (const T&) = delete;

protected:
Singleton() = default;
virtual ~Singleton() = default;
};

加锁

线程安全,但每次都有开销。

// singleton.h
class Singleton {
public:
static Singleton *GetInstance() {
lock();
if (p == NULL) {
p = new Singleton;
}
unlock();
return p;
}
private:
static Singleon *p;
Singleton() {}
Singleton(const Singleton &);
Singleton& operator= (const Singleton &);
};

// singleton.cc
Singleton *Singleton::p = NULL;

pthread_once

陈硕推荐的做法

class Singleton {
public:
static Singleton *GetInstance() {
pthread_once(&ponce_, &Singleton::init);
return value_;
}
private:
Singleton() {}
Singleton(const Singleton &);
Singleton& operator= (const Singleton &);
static void init() {
value_ = new T();
}
static pthread_once_t ponce_;
static Singleton *value_;
};
pthread_once_t SIngleton::ponce_ = PTHREAD_ONCE_INIT;
Singleton* Singleton::value_ = NULL;

DCL

double check locking. 只能用内存屏障,其他做法都是有问题的。
参见论文: http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf
普通的 double check 之所以错,是因为乱序执行和多处理器下,不同 CPU 中间 cache 刷往内存并对其他 CPU 可见的顺序无法保障(cache coherency problem)。
Singleton<T> *p = new Singleton<T>;, 那么实际有 3 步:

  1. 分配内存
  2. 构造
  3. 赋值给 p

2 和 3 的顺序是未定的(乱序执行!)。因此,如果直接赋值给 p 那么很可能构造还没完成。此时另一个线程调用 GetInstance,在 lock 外面 check 了一下,发现 p!=NULL,于是直接返回 p,使用了未初始化完成的实例,跪了。

那么,如果用中间变量转一下呢?用 tmp_p 转了下以后,tmp_p 赋值给 p 的时候,显然 p 指向的实例是构造完成了的。然而,这个 tmp_p 在编译器看来明显没什么用,会被优化掉。

关于不能自动调用析构的问题

上面的两个示例代码
( 没有考虑析构问题饿汉模式的示例代码 和 没有考虑线程安全与析构问题的懒汉模式的示例代码 ) 都有不能自动调用析构的问题.

当你运行这两个示例代码之后, 你都会发现并没有打印 “Singleton destruction”, 也就是说程序结束时并没有调用 singleton 类的析构函数的, 为什么呢?

因为 m_instance = new singleton;, new出来的东西需要delete掉, 如果加上一句 delete ct; ct = NULL;, 就会调用析构函数了.
但这种手动调用很容易忘啊, 怎么才能自动调用它的析构呢?

我们想要的是 : 自动化的正常删除该实例。

有两种方法, 我给他划分为:

  • 不需要加GC(垃圾回收)内嵌类的单例模式(推荐)
  • 需要加GC(垃圾回收)内嵌类的单例模式

需要加GC内嵌类的单例模式

我们先看第二种,

我们知道,程序在结束的时候,系统会自动析构所有的全局变量。事实上,系统也会析构所有的类的静态成员变量,就像这些静态成员也是全局变量一样。利用这个特征,我们可以在单例类中定义一个这样的静态成员变量,而它的唯一工作就是在析构函数中删除单例类的实例。
那就是定义一个内部垃圾回收GC类,并且在 singleton 中定义一个此类的静态成员。程序结束时,系统会自动析构此静态成员,此时,在此类的析构函数中析构 singleton 实例,就可以实现 m_instance 的自动释放。

#include <iostream>

using namespace std;

class singleton
{
protected:
singleton() {};
private:
singleton(const singleton&) {};
singleton& operator=(const singleton&) {};
static singleton* m_instance;
public:
static singleton* GetInstance();
~singleton()
{
printf("Singleton destruction\n");
}

class GC
{
public:
~GC()
{
printf("GC destruction\n");
if (m_instance)
{
delete m_instance;
m_instance = NULL;
}
}
};
static GC gc_singleton;
};

singleton::GC singleton::gc_singleton;

singleton* singleton::GetInstance()
{
if (m_instance == NULL)
{
m_instance = new singleton();
}
return m_instance;
}

singleton* singleton::m_instance = NULL;

int main()
{
singleton *ct = singleton::GetInstance();
return 0;
}

当然还有更好的方法.那就是下面这个不需要加GC内嵌类的单例模式.

不需要加GC内嵌类的单例模式

在 GetInstance 方法里放一个 m_instance 的局部静态变量, 然后返回他的地址, 他就可以在程序结束自动调用析构函数.
而且这种方法在C++11也能保证线程安全.

#include <iostream>

using namespace std;

class singleton
{
protected:
singleton() {};
private:
singleton(const singleton&) {};
singleton& operator=(const singleton&) {};
public:
static singleton* GetInstance();
~singleton()
{
printf("Singleton destruction\n");
}
};

singleton* singleton::GetInstance()
{
static singleton m_instance;
return &m_instance;
}

int main()
{
singleton *ct = singleton::GetInstance();
return 0;
}

总结

既要考虑线程安全又要考虑析构问题的话, 有下面几种方法 :

  • 饿汉模式+GC内嵌类
  • 懒汉模式+GC内嵌类, 然后加锁,但每个线程缓存了返回的指针,调用一次有用缓存的指针即可。
  • 懒汉模式+GC内嵌类, 然后 pthread_once

如果是C++11的话, 则可以使用局部静态变量, 因为C++11保证静态局部变量的初始化是线程安全的(C++98不保证), 而且也没有析构问题.

new和delete详解

Posted on 01-22-2015 | In Misc

c++中对new申请的内存的释放方式有delete和delete[]两种方式,到底这两者有什么区别呢?

疑问

我们通常从教科书上看到这样的说明:
delete 释放new分配的单个对象指针指向的内存
delete[] 释放new分配的对象数组指针指向的内存
那么,按照教科书的理解,我们看下下面的代码:

int *a = new int[10];
delete a; //方式1
delete [] a; //方式2

肯定会有很多人说方式1肯定存在内存泄漏,是这样吗?

针对基本数据类型

针对简单类型 使用new分配后的不管是数组还是非数组形式内存空间用两种方式均可 如:

int *a = new int[10];
delete a;
delete [] a;

此种情况中的释放效果相同,原因在于:分配简单类型内存时,内存大小已经确定,系统可以记忆并且进行管理,在析构时,系统并不会调用析构函数,
它直接通过指针可以获取实际分配的内存空间,哪怕是一个数组内存空间(在分配过程中 系统会记录分配内存的大小等信息,此信息保存在结构体_CrtMemBlockHeader中,
具体情况可参看VC安装目录下CRT\SRC\DBGDEL.cpp)

针对复杂数据类型

针对类Class,两种方式体现出具体差异
当你通过下列方式分配一个类对象数组:

class A
{
private:
char *m_cBuffer;
int m_nLen;
public:
A(){ m_cBuffer = new char[m_nLen]; }
~A() { delete [] m_cBuffer; }
};
A *a = new A[10];
// delete a 仅释放了a指针指向的这个数组本身的全部内存空间, 而且只调用了a[0]对象的析构函数,
// 但是剩下的从a[1]到a[9]这9个用户自行分配的m_cBuffer对应内存空间没有释放 从而造成内存泄漏
// 所以只有a和A[0]会变为野指针
delete a;
delete [] a; //调用使用类对象的析构函数释放用户自己分配内存空间并且释放了a指针指向的全部内存空间

再看下面代码:

class A{
//...
};
A *pa = new A();
A *pas = new A[NUM]();
  • delete []pas; //详细流程
    答案见下文总结
  • delete []pa; //会发生什么
    答案是调用未知次数的A的析构函数. 因为delete[]会去通过pa+offset找一个基于pa的偏移量找一个内存里的数据, 他假定这个内存里放了要调用析构的次数n这个数据, 而这个内存里到底是多少是未知的.
  • delete pas; //哪些指针会变成野指针
    答案是pas和A[0]中的指针会变成野指针. 因为只有这两个指针指向的内存被释放了, 也就是说, 仅释放了pas指针指向的这个数组的全部内存空间, 以及只调用了a[0]对象的析构函数

总结

所以总结下就是,关于 new[] 和 delete[],其中又分为两种情况:

  • 基本数据类型
    对于像int/char/long/int*/struct等等简单数据类型,由于对象没有destructor,所以用delete 和delete [] 是一样的!
  • 复杂数据类型类型
    • delete ptr 代表用来释放内存,且只用来释放ptr指向的内存。
    • delete[] rg 用来释放rg指向的内存!!还逐一调用数组中每个对象的destructor!!

分布式系统设计概要笔记-四

Posted on 01-10-2015 | In NP

分布式系统设计实践

基本的理论和策略简单介绍这么多,后面本人会从工程的角度,细化说一下”数据分布“、”副本控制”和”高可用协议”

在分布式系统中,无论是计算还是存储,处理的对象都是数据,数据不存在于一台机器或进程中,

. . .

分布式系统设计概要笔记-三

Posted on 01-07-2015 | In NP

分布式系统设计策略

重试机制

一般情况下,写一段网络交互的代码,发起rpc或者http,都会遇到请求超时而失败情况。

可能是网络抖动(暂时的网络变更导致包不可达,比如拓扑变更)或者对端挂掉。

这时一般处理逻辑是将请求包在一个重试循环块里,如下:
[cpp] view plain copy print?
int retry = 3;
while(!request() && retry–)
sched_yield(); // or usleep(100)

. . .

1…2324252627282930313233343536
Mike

Mike

🚙 🚗 💨 💨 If you want to create a blog like this, just follow my open-source project, "hexo-theme-neo", click the GitHub button below and check it out ^_^ . It is recommended to use Chrome, Safari, or Edge to read this blog since this blog was developed on Edge (Chromium kernel version) and tested on Safari.

11 categories
287 posts
110 tags
about
GitHub Spotify
© 2013 - 2025 Mike