执行如下命令,再重新打开Terminal即可,
sudo locale-gen zh_CN:UTF-8
另外,12.04后,左边的Launcher(启动栏)不再自动隐藏了,设置为自动隐藏的方法为:
System Settings => Appearance => Behavior => Auto-hide the Launcher (设置为on),同时将触发的灵敏度调高点,效果更好。
适用场景:
一生产者线程,一消费者线程,无锁高效的将数据从生产者线程传送至消费者线程,仅适用于一固定线程写入数据(push),另一固定线程读取数据(pop)
如:
网络游戏中RecvThread负责从socket中读取数据,传送至LogicThread中, LogicThread处理后,将需要发送的数据送至SendThread, 在LogicThread,SendThread上分别建立一个PipeQueue, 实现 RecvThread => LogicThread => WriterThread 无锁传送数据。
BTW:
1. 取名为PipeQueue的意义便限定此类如同pipe(管道), 只能一边写,另一边读。
2. __sync_synchronize() 很重要,如果去掉,g++ -O3 优化编译后的生成的程序会产生死锁。原因参考如下(具体原因我还不能够很详细的解释,待后续跟进此问题,关键词:memory barrier)
mfence指令 http://www.cnitblog.com/yuhensong/archive/2007/07/31/30994.aspx
__sync_synchronize() http://gcc.gnu.org/onlinedocs/gcc-4.6.2/gcc/Atomic-Builtins.html
GCC 提供的原子操作 http://www.cnblogs.com/FrankTan/archive/2010/12/11/1903377.html
memory barrier http://blog.sina.com.cn/s/blog_4b83fabd0100blhw.html
http://lxr.linux.no/#linux+v2.6.27.8/Documentation/memory-barriers.txt
windows下的memory barrier参考 http://msdn.microsoft.com/en-us/library/windows/desktop/ms684208%28v=vs.85%29.aspx
另外,来自msdn的关于memory barrier的解释比较清楚:
Creates a hardware memory barrier (fence) that prevents the CPU from re-ordering read and write operations. It may also prevent the compiler from re-ordering read and write operations.
3. item->data, item->next 操作的先后顺序是无锁的关键
#include <iostream> #include <cassert> #include <pthread.h> using namespace std; template<typename T> class PipeQueue { public: PipeQueue() { head = new Item(); head->next = NULL; tail = head; } ~PipeQueue() { assert(head->next == NULL); delete head; } void Push(const T& data) { Item* it = new Item(); it->next = NULL; tail->data = data; __sync_synchronize(); // important tail->next = it; tail = it; } bool Pop(T& ret) { Item* tmp = NULL; __sync_synchronize(); // important if (head->next != NULL) { tmp = head; head = head->next; ret = tmp->data; delete tmp; return true; } else { return false; } } private: struct Item { T data; Item* next; }; private: Item* head; Item* tail; }; static void* thread_writer(void* param) { PipeQueue<int>* queue = static_cast<PipeQueue<int>*>(param); for (int i = 0; i < 35000; i++) { queue->Push(i); } return NULL; } static void* thread_reader(void* param) { PipeQueue<int>* queue = static_cast<PipeQueue<int>*>(param); int i = 0; int tmp; while (true) { if (queue->Pop(tmp)) { if (i != tmp) { cout<<"data error"<<endl; break; } i++; if (i > 30000) { break; } } } return NULL; } static void test1() { PipeQueue<int> queue; queue.Push(1); queue.Push(2); int tmp; assert(queue.Pop(tmp)); assert(tmp == 1); assert(queue.Pop(tmp)); assert(tmp == 2); queue.Push(3); queue.Push(4); assert(queue.Pop(tmp)); assert(tmp == 3); assert(queue.Pop(tmp)); assert(tmp == 4); assert(!queue.Pop(tmp)); assert(!queue.Pop(tmp)); assert(!queue.Pop(tmp)); cout<<" test1 completed... "<<endl; } static void test2() { PipeQueue<int> queue; pthread_t writer, reader; pthread_create(&reader, NULL, thread_reader, &queue); pthread_create(&writer, NULL, thread_writer, &queue); pthread_join(reader, NULL); pthread_join(writer, NULL); // clean the queue int tmp; while (queue.Pop(tmp)) { } cout<<" test2 completed... "<<endl; } int main(int argc, char **argv) { test1(); test2(); getchar(); return 0; }
目的:
World上的某些函数需要序列化执行(FIFO),有利于简化逻辑,去除锁需求
解决方案:
将此类需要序列执行的方法封装为Action,加入Action队列,依次调用
代码如下,每在World上添加一个序列执行的方法时,即对的添加一个Action子类
缺点:代码较繁琐
#include <iostream> #include <tr1/functional> #include <queue> using namespace std; using namespace std::tr1; class World { public: void Update() { Action* act = NULL; // lock m_acts if (!m_acts.empty()) { act = m_acts.front(); m_acts.pop(); } // unlock m_acts if (act != NULL) { act->Execute(); delete act; act = NULL; } } void HandleMove(int x, int y) { m_acts.push(new ActionMove(this, x, y)); } private: class Action { public: Action(World* world) : _world(world) { } virtual void Execute() = 0; protected: World* _world; }; class ActionMove : public Action { public: ActionMove(World* world, int x, int y) : Action(world), _x(x), _y(y) { } private: void Execute() { _world->Move(_x, _y); } private: int _x; int _y; }; void Move(int x, int y) { cout<<"move to "<<x<<","<<y<<endl; } private: queue<Action*> m_acts; }; int main(int argc, char **argv) { World world; // IO线程调用 world.HandleMove(1, 1); // 驱动线程执行 world.Update(); getchar(); return 0; }
#include <iostream> #include <tr1/functional> #include <queue> using namespace std; using namespace std::tr1; class World { public: void Update() { Action* act = NULL; // lock m_acts if (!m_acts.empty()) { act = m_acts.front(); m_acts.pop(); } // unlock m_acts if (act != NULL) { (*act)(); delete act; act = NULL; } } void HandleMove(int x, int y) { // lock m_acts m_acts.push(new Action(tr1::bind(&World::Move, this, x, y))); // unlock m_acts } private: class Action { public: Action(tr1::function<void()> fun) : _fun(fun) { } void operator() () { _fun(); } private: tr1::function<void()> _fun; }; void Move(int x, int y) { cout<<"move to "<<x<<","<<y<<endl; } private: queue<Action*> m_acts; }; int main(int argc, char **argv) { World world; // IO线程调用 world.HandleMove(1, 1); // 驱动线程执行 world.Update(); getchar(); return 0; }
BTW:添加-std=c++0x时,可直接使用 #include<functional> 取代 #include<tr1/functional>
忘记在哪看到的这么一句话,随着年龄的增长,时间会越过越快,原因是十步的时候,每一年是生命的十分之一,而如果能活到八十岁的话,那么过一年只不过是生命的八十分之一而已。
要么就是我老得太快,要么就是拖延症太厉害,上一次写文章时下定决心,过“两几天做个总结”, 结果这几天就变得了六个月,在感叹时间的飞逝的同时,对自己的执行力感到汗颜。
非得给自己找个借口的话,那可能是因为在2011.10.21换了家公司,来了一家创业小公司,嗯,创业小公司的事总是很多的,工作也总是特别忙的,特别是前期的产品研发,在产品没上线的这段时间,研发部压力是最大的,喔,似乎现在整个公司基本都是研发部的人。
我呢,还是做服务器端的游戏开发,不过这次产品完全是从零开始,也算是一个不错的挑战,比起维护现有的代码,能亲手从零开始,设计,编码,实现一个游戏,还是比较爽的一件事。
目前产品做到差不多一半的样子,回顾这五个月来,感觉成长了不少,各方面的,遇到的问题也不少,也是各方面的。
成长一:关于新人培训
好吧,其它我也是个新人,IT行业总是需要不断的学习,知识更新,可能相对刚毕业的应届生来说,我也就多那么几年的经验吧。其实招聘的时候,我也没太纠结,有一定的基础知识,热爱编程,沟通能力强,勤奋就足够了,目前招到的人也还算基本满意。新人培训这块,我也没经验,我的想法只是套用我刚入行时,希望得到什么,然后从这方面去满足他们,尽自己的力量,帮助他们学到东西。至于企业文化之类,我只反对洗脑式的培训,工作一定是实现自我价值的同时,提升企业价值。
成长二:关于技术
好好的整理了erlang,C++,LUA方面的知识,不论哪种开发技术,经常用才能有感觉。这几个月来,特别是搭架构的初期,在解决某个问题的时候,经常陷于这样的考虑中,“这样是否合适,换种方式是否更好”,多考虑点总是好的,但考虑再多,也不及未来变化快。目前找到的解决方案就是,定时“重构”部份代码,以适应需求的变动,以及后期发现的新的问题。个人感觉,我有一定的“代码洁癖”,不能容忍代码太乱,逻辑不清,“重构”之后,看着代码变得清楚些了,心情也会突然变得很好,连晚上都能睡个好觉,不然的话,总是会觉得很忧虑。
问题一:关于进度
进度,这真是一个让程序员烦恼的词,呆了几家公司,似乎每一家开发公司都或多或小有开发进度延误的问题。我在想,如果只是单纯的写代码,不用关心进度表,直到自己觉得做得非常不错了,才交出去,应该蛮有乐趣的,不过却没有商业价值了。而如今,当自己需要来亲自把控”进度”这个东西的时候,不得不多想一些了。
延误进度的因素太多了,主要的有需求变动,工作量预估错误,致命BUG等,我想在对这方面没太多想法,只能想着提高开发效率,避免过早优化,尽量早出功能为好。
明天盘算着做一个TODOlist, 把所有工作细致的列出来,同时做一个DONElist,把每天完成的功能特性列出来,争取每天都有新的产品feature,也算是“持续交付”的一种体现形式吧。
问题二:关于身体
早上八点出门,晚上十点回家,没怎么见过白天的阳光,眼睛长时间都在屏幕前,在这样下去快要瞎掉啦。感觉近视明显加重了。
如下的简单代码展示了Zend_View中的插件函数加载机制
就是巧妙应用PHP5的魔术方法__call, 当调用指定扩展函数时,自动进行扩展的实例化,即Zend_View_Helper_*之类
<?php class View { # 存储扩展助手对象 protected $helper = array(); # 魔术方法 __call 当调用方法不存在时,自动调用扩展中的方法 public function __call($plugin, $args) { $this->addPlugin($plugin); return call_user_func_array(array($this->helper[$plugin], $plugin), $args); } public function addPlugin($plugin) { # 添加一个扩展,此处在ZendFramework中使用了规范命名与规范目录,方法自动查找 if (!isset($this->helper[$plugin])) { $class = 'Plugin_'.ucfirst($plugin); $this->helper[$plugin] = new $class; } } } class Plugin_Url { public function url($href, $label) { return '<a href="' . $href . '">' . $label .'</a>'; } } $view = new View(); echo $view->url('http://www.google.com.hk', 'GoogleInCN');
考查有如下的MySQL表
create table test(
id int auto_increment not null primary key,
balance float default 0);
增加三条新记录
insert into test(balance) values(0.01);
insert into test(balance) values(0.02);
insert into test(balance) values(0.03);
查询一下
mysql> select * from test;
+—-+———+
| id | balance |
+—-+———+
| 1 | 0.01 |
| 2 | 0.02 |
| 3 | 0.03 |
+—-+———+
问题来了:
mysql> select * from test where balance = 0.01;
Empty set (0.00 sec)
mysql> select * from test where balance >= 0.02;
+—-+———+
| id | balance |
+—-+———+
| 3 | 0.03 |
+—-+———+
1 row in set (0.00 sec)
mysql> select * from test where balance between 0.02 and 0.03;
+—-+———+
| id | balance |
+—-+———+
| 3 | 0.03 |
+—-+———+
1 row in set (0.00 sec)
mysql> select * from test where balance <= 0.02;
+----+---------+
| id | balance |
+----+---------+
| 1 | 0.01 |
| 2 | 0.02 |
+----+---------+
2 rows in set (0.00 sec)
通过如上一的系列操作,会发现 float类型,竟然不支持 = (等于),>= (大于等于),让人的疑惑的是 <=, between的后半部却是有效的。
查询了一下mysql document,找到如下解释:
If you are comparing FLOAT or DOUBLE columns with numbers that have decimals, you can't use equality (=) comparisons. This problem is common in most computer languages because not all floating-point values can be stored with exact precision. In some cases, changing the FLOAT to a DOUBLE fixes this. See Section B.5.5.8, “Problems with Floating-Point Values”.
Similar problems may be encountered when comparing DECIMAL values prior to MySQL 5.0.3.
看来 0.01 数据,mysql 默认做 double 类型进行处理。
解决方法:
正如官网上说的,将 float 类型改为 double 类型吧。
alter table test change balance balance double default 0;
改完之后的问题:
mysql> select * from test;
+—-+———————+
| id | balance |
+—-+———————+
| 1 | 0.00999999977648258 |
| 2 | 0.0199999995529652 |
| 3 | 0.0299999993294477 |
+—-+———————+
3 rows in set (0.00 sec)
数据变成如此长的精度,我只想保留两位,四舍五入一下吧。
update test set balance = round(balance, 2);
至此解决问题了,并且决定以后不再在MySQL中使用 FLOAT 类型了,至少要慎用!
这里所说的快速搭建是指建立一个私人使用的SVN服务器,不带Web访问功能,并不需要配置与Apaceh相关联的东东。
原文地址:http://www.electrictoolbox.com/install-subversion-centos/
yum install subversionsudo useradd svn sudo passwd svn # 设置密码 ******
# 切换至home cd ~ mkdir repositories # 切换至代码仓库 cd repositories
# 创建一个工程,名为 myproject svnadmin create myproject # 配置工程 vim myproject/conf/svnserve.conf # 取消掉如下两行的注释 # auth-access = write # password-db = passwd # 配置该工作的用户与密码 vim myproject/conf/passwd [users] username = password
# 切换至home svnserve -d -r /home/svn/repositories
现在,可以使用如各种SVN客户端进行版本控制了(例如windows上的 TortoiseSVN)
checkout的地址为 svn://yourhost/myproject
用户名和密码为在 myproject/conf/passwd 中所配置的内容
在PHP中使用Memcache进行数据缓存有两个客户端(pecl扩展)可供选择,一个名叫Memcache,另一个名叫MemcachedMemcached支持更多操作接口,内且内置了Memcache服务器池(集群)的管理,也许在面对多个Memcache服务器时有更好的效果。更多特性待尝试一下便知道了。
如下记录安装过程与使用方法:
由于Memcached是基于libmemcached开发的,所以应当先安装libmemcached
libmemcached的下载地址为:http://download.tangent.org/ 选择一个最新版的libmemcachedXXX.tar.gz即可
然后解包 ./configure && make && make install 即可
接下来直接用pecl install memcached 安装 memcached客户端,然后在php.ini中添加extension=memcached.so即可。
使用:(待。。)
近期评论