解决QT在VS下中文乱码的问题

我们用VS编译出来的QT程序,经常会遇见中文乱码,其实解决方法很简单:
在main函数所在文件的包含如下文件:

#include <QtCore/QTextCodec>

然后,在main函数的开头加入这么一句话就可以了。

QTextCodec::setCodecForTr(QTextCodec::codecForName("GB2312"));

之后在调用的时候,所有的字符串记得使用tr()函数包裹起来。比如:

QMessageBox::warning(this, tr("出错啦!"), errorInfo, tr("OK"));

这个是因为VS默认的编码是GB2312,而函数tr的作用,是将默认的字符串转换成QT内部的编码:Unicode,所以我们这里只需要设置好编码即可。
如果碰到一些2312里面没有的生僻字,我们也可以尝试使用Windows默认的多字符编码GB18030来解决。

Posted in 03 Binary Life. Tags: , , . 6 条评论 »

年假!音乐周!

一年一度的公司旅游,结果去的地方我已经去过了,于是抓住机会请了三天年假,想在家里好好休息休息。当然MM还要上课,所以去不了什么远的地方,于是,打算这几天在北京逛逛,然后听听音乐会就这么休息过去吧。
中央音乐学院真是一个好地方,时不时就有演出可以看,有时候还是大师来演出,质量也是相当的高,最后外加一个双休,五天听了五场,很爽很震撼。

第一天是一场几米的漫画主题音乐会,开场比较搞,为了和几米的漫画造成反差,居然用了德国战车的Engel当开头,不过后面真的很感人,期间有一首德彪西的音乐,基本上改变了我极度不喜欢他的看法,后面在看《蓝石头》+ 《空顶之光》的时候,居然被感动到流泪了,没有办法,那样干净的旋律,配上蓝石头这么煽情的剧情,再加上一个杯具的北漂程序猿,怎么不感动。当然最后被MM狂笑了一顿。。。。。一个爷们,感情神马的,哪来这么丰富。。。。。
day1-jimmy-comic:
day1-jimmy-comic

第二天是一场巴赫的室内乐音乐会,我也终于见到了真正的羽管键琴,曲目从维尔瓦蒂到巴赫,一共演出了4首室内乐,外加一首咏叹调,巴洛克时期的音乐确实很好听,但是怎么说呢。。。真的听起来都差不多,不管曲式有多么复杂,但是听起来都非常的有规律。。。于是基本是在朦脓中度过了两个小时。。。各种朦脓。。。
day2-bach:
day2-bach

第三天是美国西北大学的一个教授,Alan Chow,看介绍那是获了N多的奖,大师真的就是大师,海顿,李斯特到肖邦,各个都表现的让人震撼不已,最后由于大家掌声异常热烈,大师还加演了一首,这一首没有听过,不知道名字,但是技巧要求应该非常高,应该不属于人类可以弹的曲子吧。。。嗯。。。反正当时看的时候,我是这么认为的。。。嗯。。。我脑袋里面只有一个人物在飘。。1900。。。

第四天是侯乐天的音乐喜剧,虽然早到了半个小时,但是却已然没有座位了。。不知道为什么来了这么多人,听人讨论貌似还有某医院的二三四楼的所有员工。。。嗯。。总之就是人超多啦。。还有各种小孩子在吵闹,再加上音响效果不好。。。结果可想而知。。。很多时候实在听不太清楚上面在干什么。。不过能听清楚的地方笑点还是很多的。。
day4-music-comedy:
day4-music-comedy

第五天是一场爵士音乐会,演出了很多首歌,关键是即便是现成的爵士乐,他们也会把他改编掉,而且确实很好听,所以从头到尾都听的很high,全场一直叫声不断~
day5-jazz-night:
day5-jazz-night

另外,这几天还完成了一个很久就想做的事情,那就是把我的民谣修好,真的可惜了,一段时间没有玩它,居然琴颈就弯了,于是那天和MM还有思思老师一起去了平安里,找了家店子把琴修好了,只花了20,另外换了套Martin的弦用了50,价格算是相当公道了,琴行叫做楠木秀,老板人是个很有意思的人,原来是在重庆组乐队的,搞蓝调,赚老外的钱,后来散了,就来了北京,搞了这个琴行。。。嗯。。。

一年才一次的三天年假就这么用完了。。。用完了才发现,原来时间真的过的快。。。

Posted in 01 My Soul Apogee. Tags: . 一条评论 »

如何正确使用C++多重继承

原创文章,转载请注明:转载自Soul Apogee
本文链接地址:如何正确使用C++多重继承

C++多重继承一直是一个让人搞不太清楚的一个问题,但是有时候为了实现多个接口,多重继承是基本不可避免,当然在Windows下我们有强大的COM来帮我们搞定这个事情,不过如果你想自己实现一套类似于COM的东西出来的时候,麻烦事情就来了。

在COM里面,有两个很基础的,而且我们都会用到的特性:
1. 纯虚接口:一般使用一个只有纯虚函数的类来作为接口
2. 引用计数:在C++中一般都使用引用计数来管理对象的生命周期

这两个功能在一般设计C++接口的时候也经常用到。其实说到底,上面这两个特性牵扯到的是多重继承的二个表现:
1. 多重继承中的数据存储
2. 多重继承中的虚函数

在COM中,纯虚接口是使用的interface来定义的,引用计数是通过IUnknown接口来实现的,所有的接口都是从IUnknown这种接口中派生出来的。当我们需要某一个类实现多个接口的时候,就经常会遇见这样的多重继承:
multi-inheritance-com:
multi-inheritance-com
哦?!是不是很眼熟,ios,istream,ostream,iostream。。各种C++书籍最喜欢用的一个示例。好吧,现在我们先自己实现一个吧,看看到底要怎么使用多重继承。

多重继承中对象的的数据存储

#include <stdio.h>

class IBase
{
public:
    IBase() : n(0) {}
    virtual ~IBase() {}
    void show() { printf("%dn", n); }
    int inc() { return ++n; }
    int dec() { return --n; }

protected:
    int n;
};

class IA : public IBase
{
public:
    virtual ~IA() {}
};

class IB : public IBase
{
public:
    virtual ~IB() {}
};

class CImpl : public IA, public IB
{
public:
    virtual ~CImpl() {}
};

int main(int argc, char* argv[])
{
    CImpl o;
    IA *pA = &o;
    IB *pB = &o;

    pA->inc();
    pA->show();

    pB->dec();
    pB->show();

    return 0;
}

编译,OK,成功了!好,运行试一试。
run-result-1:
run-result-1
为什么是1和-1呢?明明n只在继承的一个类IBase里面有,一次加1,一次减一,结果不是应该是1和0么?是不是很奇怪?

这便是使用多重继承的时候经常产生的第一个问题:多副本的数据存储。
当然这个问题很好解决,只需要使用虚继承即可解决。只需要在IA和IB的定义中,在public IBase前加入virtual关键字即可。

class IA : virtual public IBase
class IB : virtual public IBase

现在再让我们来看一看运行结果:
run-result-2:
run-result-2

结果已经正确了,为什么会发生这种情况呢?虚继承到底干了些什么呢?

我们先来看看在没有使用虚继承的情况下,CImpl的在内存中是怎么样的:
cimpl-memory-normal:
cimpl-memory-normal

对于普通的public继承(非虚继承),C++会将基类和派生类的内容放在一块,合起来组成一个完整的派生类,在内存上看,它们是连在一起的。按照这样的规则,在这里,IA和IB中就会各包含一个IBase,而IA,IB和CImpl中的部分内容又共同组成了CImpl。在将CImpl对象o的指针转型成IA的指针pA过程中,指针将被移动到IA所在的部分区域,同样在转型成IB的过程中,指针将被移动到IB所在的部分区域(也就是说,转型之后,指针的值都不一样)。在之后的操作中,pA操作的便是IA这个部分中的IBase,而pB操作的便是IB这个部分中的IBase,最后IA部分中的IBase变成了1,而IB部分中的IBase变成了-1。所以输出的结果也就变成了1和-1了。

之后我们修改成了虚继承,看看到底发生了什么?
cimpl-memory-virtual:
cimpl-memory-virtual
原来的IA和IB中的IBase部分变成了一个指向基类的指针,而基类也变成了一个单独的部分。这样一旦对基类做任何的修改,都会通过这个指针重定向到这个独立的基类上去,于是,就不存在多副本的数据存储了,这个诡异的问题也就解决了。但是当然从这个图上我们也可以看到,使用虚继承后,访问数据多了一次跳转,这多出的一次跳转将导致效率的下降一倍甚至更多,所以如果一个类使用的非常频繁,很明显应该尽量避免使用虚继承。

二义性

当然数据的存储只是使用多重继承中遇到的一个问题,现在我们来看另外一个问题,函数的二义性。
首先我们先把数据的存储抛开,单纯的来看一个只有函数的继承关系。

class IBase
{
public:
    virtual ~IBase() {}
    void foo() { }
};

class IA : public IBase
{
public:
    virtual ~IA() {}
};

class IB : public IBase
{
public:
    virtual ~IB() {}
};

class CImpl : public IA, public IB
{
public:
    virtual ~CImpl() {}
};

int main(int argc, char* argv[])
{
    CImpl o;
    o.foo();    // 直接调用CImpl的foo函数

    return 0;
}

编译一下,试试。
error C2385: ambiguous access of ‘foo’
could be the ‘foo’ in base ‘IBase’
or could be the ‘foo’ in base ‘IBase’
error C3861: ‘foo’: identifier not found

出错了!杯具。。为什么?错误还这么奇怪,神马叫做可以是IBase中的foo又可以是IBase中的foo呢?

这就是使用多重继承的时候经常产生的第二个问题:二义性。
在使用多重继承时,如果有两个被继承的类拥有共同的基类,那么就很容易出现这种情况。那什么是二义性呢?
我们先来看一个更简单的继承关系:

class A
{
public:
    void foo();
};

class B : public A
{
public:
    void foo();
};

class C : public B
{
public:
    void foo();
};

我们可以把继承关系中,两个类之间沿着基类方向的相隔的继承级数看成一个距离,那么C到A的距离是2,B到A的距离就是1。当然距离不能为负。
当我们对ABC中某个对象调用foo函数的时候,编译器会优先选择离当前指针类型的距离最短的一个函数实现去调用,也就是说,foo函数的查找路径是C->B->A,找到一个最近的去调用。
而对于我们当前这个继承关系来说,IA和IB还是各包含一份IBase的实例,虽然在内存里这里仅仅是包含一份数据,但是在编译的过程中,IA和IB中还包含了一份从IBase中继承下来的函数列表。所以有两个包含有foo函数类与CImpl类的距离是一样的,所以在对CImpl调用foo函数,就产生了所谓的二义性,除非我们指定使用IA::foo或者IB::foo,否则编译器将无法决定使用哪一个基类的foo函数。

o.IA::foo();    // 指定调用CImpl从IA部分继承过来的foo函数,这样就可以编译通过了。

当然如果我们这样写代码也是不行的:

IBase *pBase = &o;    // 指针转义时的二义性,不知道是使用IA中的IBase部分,还是IB中的IBase部分
o.inc();                    // 数据访问时的二义性,不知道是访问IA中IBase部分的n,还是IB中IBase部分的n

多重继承中的虚函数

既然直接使用多重继承会有如此多的问题,那么我们能不能通过虚函数来解决这个问题呢?

这里小小的提一下,刚刚二义性里面说到两个类的距离,对于编译器来说,一般是找离当前的类距离最近的函数实现来调用(或者数据来访问),而虚函数则是让编译器做相反的事情:找一个离当前类反向距离最远的函数实现来调用。

好,我们先把上面的程序做一点点小改变,把foo()函数变成一个虚函数,看看有什么变化。

class IBase
{
public:
    virtual ~IBase() {}
    virtual void foo() {}    // 变成虚函数了
};

编译,结果还是失败。
error C2385: ambiguous access of ‘foo’
could be the ‘foo’ in base ‘IBase’
or could be the ‘foo’ in base ‘IBase’
error C3861: ‘foo’: identifier not found

产生问题的原因依然是二义性。即便换成virtual函数,也不能改变二义性这个问题。为什么呢?
因为我们是用的.运算符来访问的,而不是用指针,所以这里虚函数和普通函数没有任何区别。=.=。。。
好,我们再来小小的修改一下,把他变成指针,让他通过虚表去访问,看看行不行。

CImpl *p = &o;
p->foo();

编译,结果。。。还是一样失败。。。
好吧,我们可以把调用foo()的几句话都去掉,来看看CImpl中生成的虚表到底是个什么样子。
debug-result-vptr-1:
debug-result-vptr-1

在这个实例中,IA和CImpl部分公用一个虚表,而IB则使用另外的一个虚表(两个虚表这个特性主要是在指针类型转换的时候有用,这里就不说了)。
在这IA的虚表中存在一个指向IBase::foo()的指针,在IB的虚表中也存在一个指向IBase::foo()的指针,所以在CImpl中,可以找到两个IBase::foo()函数的指针,这样,编译器就无法确定到底应该使用哪一个IBase::foo()函数作为他自己的foo()函数了。二义性也就产生了。

既然如此,那解决起来就没有什么别的办法了,只能把foo函数的最终在CImpl中实现一次了。

class CImpl : public IA, public IB
{
public:
    virtual ~CImpl() {}
    virtual void foo() { }
};

int main(int argc, char* argv[])
{
    CImpl o;
    o.foo();

    CImpl *p = &o;
    p->foo();

    return 0;
}

编译一下,通过了!对于o.foo()来说,这当然是意料之中,离CImpl距离最近的foo函数实现,就是CImpl自己嘛,当然没有问题。
对于后面这个p->foo()的调用,编译器现在也已经可以决定对于CImpl这个类来说,离他最远的foo函数调用是谁了——也是他自己。
所以这里就不会产生二义性的问题了。

在多重继承中编译器对this指针的修正
这里再让我们来看看这次编译出来的虚表,看看还有什么发现。
debug-result-vptr-2:
debug-result-vptr-2

0x004112a3 [thunk]:CImpl::foo`adjustor{4}’ (void) *
这个看上去很怪的函数是什么呢?我们反汇编一下他看看。
virtual-function-wrapper:
virtual-function-wrapper

这里可以看到有一句汇编指令:sub ecx, 4。这条指令的左右其实是在修正this指针。
因为从IB的虚表来的请求,this指针都是指向CImpl中IB的部分,而当调用CImpl中的foo函数时,如果还使用IB的this指针,那么程序就会出错,所以这里需要先将this指针修正为CImpl所在的地址,才能调用CImpl的foo函数。

在程序运行的时候,this指针一般被存储在ecx寄存器中,或者当前函数的第一个参数传递进去,不过不同的语言或者不同的编译器编译出来的代码可能会不一样。

我们这里的析构函数都是虚函数,所以我们还可以在截图中看到,编译器会对析构函数做同样的处理。

如何同时解决数据访问和二义性问题呢

貌似到现在都只提到最简单的一种多重继承的情况,但是实际上我们已经遇到了很多的问题了,既然多重继承中会有这么多问题,那我们有没有什么比较通用的方法能把他们一起解决了呢?
方法肯定是有的:
1. 使用虚继承
这算是一种确实可行的方法,只是说会带来额外的时间和空间的开销,访问任何一个数据,都需要通过虚继承表进行跳转,不过一般来说够用了。

2. 虚函数当接口,继承多个接口,统一实现
这个思想就类似于COM了,只是说COM用的是纯虚函数,对于那些会产生二义性的类,我们在最后都实现一边,这样就不会有问题了。这样带来的时间开销也仅仅是调用时查询一次虚表。但是麻烦的地方就是,有时候继承一下,你可能就要实现一下了,比如引用计数神马的,当然你也可以通过模版来简化你的代码。

class IBase
{
public:
    virtual ~IBase() {}
    virtual void show() = 0;
};

class IA : public IBase
{
public:
    virtual ~IA() {}
    virtual int inc() = 0;
};

class IB : public IBase
{
public:
    virtual ~IB() {}
    virtual int dec() = 0;
};

class CImpl : public IA, public IB
{
public:
    CImpl() : n(0) {}
    virtual ~CImpl() {}
    int inc() { return ++n; }
    int dec() { return --n; }
    void show() { printf("%dn", n); }

private:
    int n;
};

3. 通过纯虚函数实现模版方法,将函数转移
这种实现比较复杂,wtl中用的比较多,一般是用在引用计数上,好处很明显,就是可以继承,不用每个类都实现一个引用计数,而只用将新的基类的引用计数转移至原本存在的类上就可以了。

class IBase
{
public:
    virtual ~IBase() {}
    void foo() {}
};

class IA : public IBase
{
public:
    virtual ~IA() {}
};

class IShifter
{
public:
    virtual ~IShifter() {}
    void foo() { do_foo(); }

protected:
    virtual void do_foo() = 0;
};

class IB : public IShifter
{
public:
    virtual ~IB() {}
};

class CImpl : public IA, public IB
{
public:
    virtual ~CImpl() {}
    void foo() { IA::foo(); }

protected:
    virtual void do_foo() { IA::foo(); }
};
Posted in 03 Binary Life. Tags: , . 一条评论 »

记忆碎片

memento:
memento
这部经典的片子已经出来10多年了,一直久闻他的大名,却没有什么时间看,直到前天晚上,和MM一起看了这部经典作品,看完之后确实有些感触。
10多年前的片子,剧透神马的应该就是浮云了,我就随便乱说了。

故事的主人公是一个没有记忆力的人,在一起事故中,主人公失去了自己的妻子,也失去了记忆力,他只能记住很短一段时间内发生的之后,一旦过了一会儿,他就不知道自己在哪,在干什么,所以他只能把他知道的事情记录在纸上,利用这些记录下来的纸片帮助他对当前的事情做决定。设定什么的大概是这样。于是故事开始了。
和普通的影片不同,这部片子有两条时间线,一条从最开始向后叙述,另外一条从后向前叙述,最后在中间汇合,并交待故事的前因后果。开始主要是后面这条线,我们可以看到主人公是怎么样利用他身上的纹身,带在身上的照片来帮助自己继续的正常的生活,可是事情越向前发展,越不对,那些原本帮助他记忆的纸片,却成为了诱导他犯错的原因。仅仅一句“不要相信他的谎言”,却导致他对后面所有发生的事情都推断错误,一个在帮助他的人,却被他杀害了,而一直在玩弄他的人还好好的活着。而影片最后发现他在那次事故中并没有失去他的妻子,他的妻子是被他自己害死的,而他却把他和他的一个案子里的人记混了,误以为他的妻子是在那次事故中死掉的,最后一直追杀杀害他妻子的凶手。一切就是这么戏剧。。。

看完之后我就在想,那既然短期的记忆实际上记得并不会很长久,笔记也不一定能帮助我们正常的工作,长期记忆更是经常容易记混,我们应该如何来对正在发生的事情做出正确的判断呢?
从影片看来似乎是不可能的,导演也没有给我们一个方法,但是影片却确实给我们留下了一个答案:
按照自己的规则去过自己的人生吧,即便它是错的,但是只要你自己坚持下去,你自己就会觉得它有意义。只要自己觉得一切是有意义的,这就足够了。
memento-truth:
memento-truth

Posted in 06 Screen Sucker. Tags: . 没有评论 »

Chrome学习笔记(一):线程模型,消息循环

原创文章,转载请注明:转载自Soul Apogee
本文链接地址:Chrome学习笔记(一):线程模型,消息循环

看Chrome已经有一段时间了,但是一直都没有沉淀些内容下来,是该写写笔记什么的了,免得自己忘记了。看的都是Windows平台下的代码,所以记录也都是记录的。。。废话。。
那么首先,先从最基础的东西记录起吧:Chrome的线程模型和消息循环。

多线程的麻烦

多线程编程一直是一件麻烦的事情,线程执行的不确定性,资源的并发访问,一直困扰着众多程序员们。为了解决多线程编程的麻烦,大家想出了很多经典的方案:如:对资源直接加锁,角色模型CSPFP等等。他们的思想基本分为两类:一类是对存在并发访问资源直接加锁,第二类是避免资源被并发访问。前者存在许多问题,如死锁,优先级反转等等,而相对来说,后者会好很多,角色模型,CSP和FP就都属于后者,Chrome也是使用后者的思想来实现多线程的。

Chrome的线程模型

为了实现多线程,Chrome思路是简单且尽可能的少用锁,所以它在实现中并没有使用如角色模型之类的复杂的结构,而只是引入了自己的消息循环作为多线程的基础。它足够简单,方便使用,而且很容易实现跨平台。
相比平时的消息循环(如:Windows的消息循环,Linux中的epoll模型),它唯一增加的功能就是可以运行自定义的任务:Task。
如果在一个线程里面需要访问另一个线程的数据,则把接下来要运行的函数和参数包装成一个Task,并将其传递给另外一个线程,由另外一个线程来执行这个Task,从而避免关键数据的并发访问,而又因为任务执行是有顺序的,这样就保证了代码执行的确定性。
chrome-messageloop-task-simple:
chrome-messageloop-task-simple
其实,这就是一个典型的Command模式,而通过这个模式,简单的在线程之间传递Task,就实现了Chrome的多线程模型。

Task

1. Task
为了统一所有消息循环中的任务调用方式,所有的任务的基类都是这个Task类,他唯一的方法就是run(),MessageLoop只需要调用这个虚函数即可。
如果为了简化大家的开发,Chrome可谓下足了功夫,光是一个Task,就提供了各式各样的派生类供大家使用,并提供了良好的实现。

  • 派生出来的Task有:CancalableTask,ReleaseTask,QuitTask等等。
  • 根据调用条件的不同,将Task又分为即时处理的Task、延时处理的Task和Idle时处理的Task。
  • 为了简化开发,还引入了RunnableMethod,封装对象的方法,减少我们自己实现Task的时间。
  • 调用PostTask时,还需要传入一个TrackedObject,用于追踪Task的产生位置,为调试做准备。

2. RunnableMethod
RunnableMethod是一个很非常有用的类,这个方法通过模版将一个对象和他的方法和参数封装成一个Task,抛入另外一个线程去工作,其中为了保证对象的生命周期,对象的指针必须有引用计数,如果这个Task跨线程调用的话,这个引用计数必须是线程安全的。参数则通过Tuple来进行封装。在Task执行的时候通过另外一个模版将Tuple解开成参数即可。

线程和消息循环

Chrome将其线程分为了三类:普通线程,UI线程和IO线程。他们之间的区别是:

  • 普通线程:只能执行Task,没有其他的功能。
  • UI线程:所有的窗口都需要跑在UI线程上,它除了能执行Task以外,还能执行和界面相关的消息循环。
  • IO线程:和本地文件读写,或者网络收发相关的操作都运行在这个线程上,它除了能执行Task以外,还能执行和IO操作相关的事件回调。

由于这三类线程中Task的执行部分基本是一样的,而其他的功能却完成不同,为了实现这不同的三类线程,Chrome将消息循环分成了两个部分:MessageLoop和MessagePump。
chrome-thread-and-messageloop:
chrome-thread-and-messageloop
MessagePump被提取出来负责执行Task的时机和处理线程本身的消息 ,如:UI线程的Windows消息,IO线程的IO事件。
MessageLoop则仅仅是做Task的管理,它实现了MessagePump的Delegate的接口,这样MessagePump就可以告诉MessageLoop何时应该处理Task了。
另外实现上虽然Chrome为这三种线程实现了三套MessageLoop,但是它们之间的区别,也仅限于暴露出现的MessagePump的接口不同而已。
chrome-messageloop-class-diagram:
chrome-messageloop-class-diagram

消息循环之MessageLoop

1. 减少锁的请求
一般我们在实现任务队列时,会简单的将任务队列加锁,以避免多线程的访问,但是这样每取一次任务,都要访问一次锁。一旦任务变多,效率上必然成问题。
Chrome为了实现上尽可能少的使用锁,在接收任务时用了两个队列:输入队列和工作队列。
当向MessageLoop中内抛Task时,首先会将Task抛入MessageLoop的输入队列中,等到工作队列处理完成之后,再将当前的输入队列放入工作队列中,继续执行。
chrome-messageloop-task:
chrome-messageloop-task
这样,就只有当将Task放入输入队列时才需要加锁,而平时执行Task时是无锁的,这样就减少了对锁的占用时间。

2. 延时任务
为了实现延时任务,在MessageLoop中除了输入队列和工作队列,还有两个队列:延迟延迟任务队列和需在顶层执行的延迟任务队列。
在工作队列执行的时候,如果发现当前任务是延迟任务,则将任务放入此延迟队列,之后再处理,而如果发现当前消息循环处于嵌套状态,而任务本身不允许嵌套,则放入需在顶层执行的延迟任务队列。
一旦MessagePump产生了执行延迟任务的回调,则将从这两个队列中拿出任务出来执行。

消息循环之MessagePump

MessagePump是用来从系统获取消息回调,触发MessageLoop执行Task的类,对于不同种类的MessageLoop都有一个相对应的MessagePump,这是为了将不同线程的任务执行触发方式封装起来,并且为MessageLoop提供跨平台的功能,chrome才将这个变化的部分封装成了MessagePump。所以在MessagePump的实现中,大家就可以找到很多不同类型的MessagePump:如MessagePumpWin,MessagePumpLibEvent,这些就是不同平台上或者不同线程上的封装。

Windows上的MessagePump有三种:MessagePumpDefault,MessagePumpForUI和MessagePumpForIO,他们分别对应着MessageLoop,MessageLoopForUI和MessageLoopForIO。

下面我们从底层循环的实现,如何实现延时Task等等方面来看一下这些不同的MessagePump的实现方式:

1. MessagePumpDefault
MessagePumpDefault是用于支持最基础的MessageLoop的消息泵,他中间其实是用一个for循环,在中间死循环,每次循环都回调MessageLoop要求其处理新来的Task。不过这样CPU还不满了?当然Chrome不会仅仅这么傻,在这个Pump中还申请了一个Event,在Task都执行完毕了之后,就会开始等待这个Event,直到下个Task到来时SetEvent,或者通过等待超时到某个延迟Task可以被执行。

2. MessagePumpForUI
MessagePumpForUI是用于支持MessageLoopForUI的消息泵,他和默认消息泵的区别是他中间会运行一个Windows的消息循环,用于分发窗口的消息,另外他还增加了一些和窗口相关的Observer等等。
各位应该也想到了一个问题:如果在某个任务中出现了模态对话框等等的Windows内部的消息循环,那么新来的消息应该如何处理呢?
其实在这个消息泵启动的时候,会创建一个消息窗口,一旦有新的任务到来,都会像这个窗口Post一个消息,这样利用这个窗口,即便在Windows内部消息循环存在的情况下,也可以正常触发执行Task的逻辑。
既然有了消息窗口,那么触发延时Task的就很简单了,只需要给这个窗口设置一个定时器就可以了。

3. MessagePumpForIO
MessagePumpForIO是用于支持MessageLoopForIO的消息泵,他和默认消息泵的区别在于,他底层实际上是一个用完成端口组成的消息循环,这样不管是本地文件的读写,或者是网络收发,都可以看作是一次IO事件。而一旦有任务或者有延时Task到来,这个消息泵就会向完成端口中发送一个自定义的IO事件,从而触发MessageLoop处理Task。

小可爱啊小可爱

昨天打完篮球回来,mm居然用ipad给我画了一幅画当作5月20号的礼物,哈哈。。小可爱啊小可爱,你能不这么可爱么?
不过为什么说画的是我?这到底哪里像我了?

20110522-093629.jpg

Posted in 02 My Soul Perigee. Tags: . 一条评论 »

iPad入手!

感谢MM,帮我买了一个iPad2,了却了我一大心愿,开心(^_^)
玩了一天,感觉确实不错,屏幕很大,做很多事情都很舒服,比如写博客,发右键,看电子书。
先测试一下发博客,WordPress的应用在iPad上用起来感觉还挺舒服的,自带的输入法使用起来也很不错,整句输入貌似准确率还可以啊。不过遗憾的是暂时还没有破解,神马东西都要钱。。(T_T)

20110518-133926.jpg

Posted in 01 My Soul Apogee. Tags: . 4 条评论 »

樱花节去看樱花

快四月份了,传说中的樱花终于盛开了,于是和MM一起跑到玉渊潭,准备来一场浪漫的樱花之旅。结果。。。
这是我幻想中的樱花节:
河河路花花路路路花花路河河
河河路花花路路路花花路河河
河河路花花路人人花花路河河
河河路花花路路路花花人河河
河河路花花路路路花花路河河
河河人花花路路路花花路河河
河河路花花路路路花花路河河
河河路花花路路路花花人河河
河河路花花路路人花花路河河
河河路花花路路人花花路河河

结果现实中的樱花节是这样的:
土土土土土土土土人土土土土
人土树土土树树土土土土人土
土人树土土土土土土树土土土
土土土土土树树人人土人土土
土土树树土人土人人人人人土
人土土土土土人树树人树人土
土土土土树土人人人花人人土
土人树土土土土人人树人人土
土土人土土人土人人人人土土
土土土树土土树土树土树土土
土土人土土土土土土土土土土

=。=||| 真是坑爹啊!不过,凑近了看,看是有好看的景色的。。嗯。。凑近了看。。近了看。。了看。。看。。
yyt-sakura:
yyt-sakura

[WordPress Plugin] UBB Master——管理你自己的UBB代码

前几天想在wordpress里面插入一小段ubb来生成一些链接,结果发现居然没有,于是自己写了一个很弱的小插件来管理自己的ubb代码。语言暂时只有英文,不过内容是中文也是可以用的。
Wordpress的用户信息真的不是很友好啊,找了半天也没有找到我自己的项目列表,寒。
现在代码总算是提交了,不过也不知道什么时候才能生效,姑且先这么放着,看看明天结果如何。

============= 我是欢乐的分隔线 =============

插件名称:ubb-master
插件地址:http://wordpress.org/extend/plugins/ubb-master/
插件用法:
一段典型的ubb代码,一般如下:
[search]put your keyword here…[/search]

然后我们就可以这样替换这段ubb代码了:
!{content}: 在[ubb]和[/ubb]标签之间的内容。
!{encoded_content}: 经过urlencode之后的!{content}
!{attr:attribute_name}: [ubb]标签中名为attribute_name的属性
!{encoded_attr:attribute_name}: 经过urlencode之后的!{attr:attribute_name}

比如:
如果我们将search的ubb格式定为如下这段代码:
<a target=”blank” href=”http://www.google.com/search?ie=UTF-8&q=!{encoded_content}”>!{content}</a>

那么上面这段ubb在输出中将会被转变为如下的内容:
<a target=”blank” href=”http://www.google.com/search?ie=UTF-8&q=put+your+keyword+here%26%238230%3B”>put your keyword here…</a>

这样,我们就可以自己定义自己的ubb代码,而不用四处找适合自己的wordpress插件了。

又要上班了

春节还真是快,一下子就又结束了,回到家,本来打算看看《Windows核心编程》给自己好好充充电,结果也没能如愿,摆弄了下博客,就又要回去上班了,囧啊。

算上原来在深圳的半年,跟着这个项目已经有一年的时间了,期间经历了很多,这里真的要感谢很多人对我的帮助,让我这个Windows小白也能在这个项目上发挥自己的作用。马上又要开始上班了,但愿新年我能表现的更好。

兔年要做机灵的小兔子,不能做成了一个2!

Posted in 01 My Soul Apogee. Tags: . 4 条评论 »