Say a automation test failure was issued and assigned to you. After some investigatons, you found the the failure was due to a postponed product bug. What should you do?
![]() |
![]() |
Say a automation test failure was issued and assigned to you. After some investigatons, you found the the failure was due to a postponed product bug. What should you do?
我和Gary打算开发一款适合小应用的,快速存储解决方案 -- Small App Storage。所有的数据存取都在C#对象的存储和访问间完成。于是今天和Gary讨论了一些sas的具体需求以及存储接口的问题,产生了几个主要意见分歧:
1. 何种类型可以被存储:
我的意见是,在C#中模拟JS中的几个基本类型,限制只能存储这几种基本类型。基本类型有 String, Number, Boolean, Array, Object。并在C#中模拟这些JS基本类型的特性。
理由是这些基本类型基本上可以满足所有小应用开发的需要。而且纵观现在的数据库,也基本提供了类似的类型,如char, text, bool, int等,而二维表与表间的关系可以由Object对象间的引用来表达。
Gary的意见是不要和类型绑定的太死,他希望所有的C#类型都可以被存储。而这样就会产生几个问题:
1)C#中的类型众多,如何存储?考虑到enum,struct,还有各种类,还有对象的引用关系,存储文件的结构上也会变的很复杂,
2) 如果数据在存储时存储的类是ClassOld,在之后应用进行了refactor,ClassOld的类名变成了ClassNew,数据如何还原?
2 第二个分歧也是由第一个分歧产生的。我希望每当同一个c#对象付给存储对象时,不会新生成一个数据记录。伪代码如下:
Storage s = new Storage(AppId);
Number n = new Number(234);
s.Root["A"] = n;
n.Value = 567;
s.Root["B"] = n;
我的想法是s.Root["B"]和s.Root["A"]能取到同一个对象,既s.Root["A"].Value最后的指为567。
这样的好处是和C#中的操作类似,因为Number为引用类型,应该指向同一个对象。而且本来应用的目的就是模拟C#对象操作,在对象操作的瞬间就存储数据。
但Gary认为Storage为存储对象,以一般存储数据库的行为思考,他应该是建立了一个新数据。而且如果分歧1中他的意见成立,那么就可以直接付值值类型。
s.Root["A"] =234;
另外,如果每次新生成一个数据,那么就可以用Xml很容易的表示对象的层次结构。
而我觉得,即使直接付值类型,他也会被boxing,变成reference type。而且,如果既要存储对象层次关系,又要复制值。那么当我存储一个环状链表时,程序就会进入死循环,不停的将引用当成新数据写入。而且如果我们在1.0版中不对引用进行支持,那么就同AppSettings没有区别了,也只能存储值类型数据。
3. 关于数据存储的接口,我认为它不用管C#对象的层次关系,所有的存储和读取都以对象ID号进行。在new Storage时,得到一个Root对象,根据Root对象的Children Information可以得到Children Reference ID和Children Keys。根据Children ID又可以得到下一级的Children ID。从而实现完全对对象引用的模拟,从而解决分歧2中引用到同一个数据难的问题。
Gary的观点认为,数据存储时应该提供对象层次的关系,从而在XML中做对应的层次映射。
从根本上说,Gary倾向与用Xml原由的层次关系来描述对象。而我倾向与用一个线行的表结构来描述所有对象,就有点像Heap一样,Object的实际内容都存放在Heap里,而用一个Reference来表明他们之间的关系。
今天在做内存释放的工作时,因为盲目相信经验和自己的理解,居然弄错了几个地方。一直以来,我以为Using statement结束后调用的是finalizer,还根据GC释放的原理:“如果对象的reference不为0,那么该对象永远不会被释放”大大的探讨了一翻工作中碰到的问题,结果后来被老大指出只要类实现了IDispose,那么using结束后是能够强制调用Dispose的。通过这个问题,也让我突然发现我对Dispose理解上的错误,重新思考了Dispose和Finalize的区别 -- Dispose和Finalizer的主要区别在于释放的时机:
Dispose是的主要目的是“立刻运行释放”,应该释放尽可能多的资源,但主要还是去释放“不立刻释放会影响进程运行”的资源,比如连接的关闭工作等。而一般托管对象能够被GC自动释放,而且一般不会影响当前软件运行,所以Dispose中不释放也不会影响系统运行。
而Finalizer中则基本上是释放“资源不被需要时”释放的资源。举个实际点的例子,LoadLibrary可以用来在运行时加载dll并调用其中的interfaces,但有个问题是我们很难控制何时unloadlibrary,因为这些interface在整个运行时都可能被调用到,由loadlibrary加载进来的interfaces只有在程序退出时才“不被需要”。在这种情况下我们就可以利用单例的finalizer去做unloadlibrary的工作,因为单例的生存周期是和整个appdomain一致的,当appdomain被unload时,interfaces和它所依附的单例自然也没有存在的必要了。(具体的解释看这里)另外,为了安全的原因,Finalizer也会顺带调用Dispose函数,以便在最后的释放时机把之前可能忘记释放的资源一并释放。
|
|
Dispose |
Finalizer |
|
不释放资源对进程的影响程度 |
高 |
低 |
|
资源生命周期 |
短,但频繁 |
长,约等于整个对象的生命周期 |
|
进程对资源的需求 |
临时的或离散的 |
需求时间长 |
|
释放的时机 |
一旦意识到资源的存在可能影响系统运行,由程序员手工显示释放 |
当对象的不可以访问时,即reference为0,由GC释放在未知时间释放。 |
|
传递给开发人员的信息 |
当前对象不关闭有危险 |
当前对象不要关闭,释放的工作我自己会处理! |
还有一些释放时需要注意的地方:
References:
http://msdn.microsoft.com/en-us/library/yh598w02.aspx
前段时间Gary问了我个面试题:“ASP.NET Session ID会不会不停的变化?”据说某人的答案是“IE浏览器访问时会变化”听了后立刻就觉得这说法应该不靠谱。
先看看Session的工作机制。Session本身分成两部分,一个是ID,一个是Data,ID部分是存储在客户浏览器请求的header中的,比如存放在Cookie或者是请求时的url中;而Data是存放在服务器上的某个dictionary里。当用户请求的时候,用户向服务器提交SessionID,服务器得到SessionID并以它为key在Dictionary里查找数据。因此,SessionID是用户和服务器状态数据的唯一联系,这个SessionID没必要频繁变化,也不能频繁变化。试想一下,假如SessionID是变化的,并且我们用的是Cookless的工作方式,那么SessionID就是在Url中体现的,也就意味着当服务器响应html给客户端的时候,就必须将所有链接中的SessionID提前变更,然后再响应数据。这时用户只开一个窗口刷新是没问题的,因为当前页的链接中总是包含正确的SessionID,但是假如用户右键链接打开新窗口,再回到原页面工作呢?这时就出问题了。因为用户打开新窗口后,向服务器发送了请求,而服务器在收到请求后,改变了SessionID,这时用户切回老窗口,老窗口链接中的SessionID还是没有变化,再点击,全是死链。因此,“SessionID是不断变化”的这种说法根本不可能成立。
为了证明我的观点的正确性,Gary到公司后特意做了实验,可结果却出乎意料。在一个ASP.NET页面上加入一个LinkButton或者Button,用Response.Write(Response.Session.SessionID)的方式显示SessionID,结果每次点击按钮SessionID都会改变。
这是为什么呢?要理解这个问题,首先要理解ASP.NET中Response的工作机制。ASP.NET中Response在默认情况下,响应的数据并不是在调用Response.Write时就传回客户端,而是等所有的渲染工作完成后,才一股脑儿把响应http报文header和data的内容返回给客户端。由于在用户没有提供SessionID的情况下,任何和Session有关的操作被触发时ASP.NET都会创建一个新的ID,因此Gary的实验中能得到SessionID数据,但可以注意到在Gary的实验中,并没有往Session中存放数据,只是查询了SessionID,所以,ASP.NET在开始响应时,发现并没有必要记录该用户的状态,所以释放了Session对象,结果就是在响应的报文中,用户浏览器并没有收SessionID的信息,再次请求时ASP.NET由于没有收到SessionID信息,再次创建了SessionID,周而复始,以至于看到“SessionID不断变化的假象”。
那么有没有办法在没有存放Session数据的情况下,就响应SessionID给客户端呢?当然也是有办法的。Response对象下有一个Buffered属性,在该属性被设置为false时,ASP.NET会立刻把现阶段收集到的报文header部打到客户端,之后的代码中就不能有涉及到操作头部的信息了,否则就会抛出Exception。因为ASP.NET在flush stream时,并不知道接下来的代码中会不会有涉及到Session的操作,所以即使之前没有存储Session数据,ASP.NET也会把SessionID立刻打回客户端,
这种立刻响应的连接方式叫做chunked data response,也是做comet数据推送的基石。
原来还有document.adoptNode()啊....
1. Delegate和Event的区别?
Event不可以由外部触发,Event支持add和remove访问器。
2. Dispose, Finalizer, Destructor的区别?
3. Int[,]和int[][]的区别?
4. 静态构造函数和非静态构造函数的区别?
5. 函数中使用using和.cs文件开头使用using的区别?
6. String 和string的区别?(注意大小写)
7. String是值类型吗?例题:
Static void test(string b)
{
b = ”b”;
}
String a = “a”;
Test(a);
8. 单例和只用静态的区别?(非托管资源的释放与单例)
9. 假设Obj是Object class的实例, Obj.GetType() == typeof(Object) 与Obj is Object有什么不同?
10. string test = string.Empty 与 string test = "" 有什么不同?
11. ref关键字与out关键字有什么区别?
12. 带ref关键字的引用类型传值和不带ref关键字的引用类型传值有什么区别?
1. postMessage (XDR)
多个window或者多个worker之间发送消息
2. Worker (Firefox3 b2支持)
类似Thread
3. registerProtocalHandler
能够让mailto:test@test.com这样的连接指向自己的WEB应用.
4. Native json parser and stringifier
5. querySelector Api
现在DOUFU中的碰撞测试是基于一个轮询机制,负责管理步调的类(PLAYGROUND)循环调用数组中的GameObject的步调函数,然后步调函数处理移动,得到下一个移动的坐标,将移动坐标传给负责碰撞测试的函数,询问是否可以移动。如果可以移动,则将GameObj的坐标设置为期待坐标,否则不改变。依此调用每个GameObject直到任务完成。
这样的设计有个严重的问题,就是碰撞测试执行的次数实在太多了,每个GameObject需要移动,就必须调用一次,并且碰撞测试函数将其与其他所有GameObject进行一次碰撞,这样导致随着GameObject数目N的上升,碰撞测试的调用次数将是N*(N-1),
另外一个问题是,这样的机制有可能导致游戏不太公平,因为先入数组的GameObject将获得更高的优先级别抢占指定的位置。
那么怎么可以减少碰撞的次数和解决上述问题呢?上次优化中已经考虑过这个问题,但是没有考虑到点子上,通过增大步长和只判断前方物体来减少运算量,这样的做法虽然可行但效果不明显。
经过在拉屎时候的思考,决定把机制改一改,使用预测机制来减少预算量,既是,每个GameObject先预测出可能移动的坐标,由碰撞测试程序进行一次性测试,最后把结果返回给各个对象,这样一来,由于进行计算时的坐标已经是预期坐标,不存在优先权问题,同时也大大减少了预算量,降低运算量到N +(N-1)+(N -2)。。。+ M (M > 1)。
同时还可以结合脏记录法,标记出没有移动过的物体,只计算移动过的GameObject。进一步降低运算量。
I'd like to share with you a interesting bug of IE, which was found while I am writing my own JSON implementation.
Only 20 lines javascript code cause IE 6 and IE 7 browser crash!
////////////Code start
callback = function(data)
{
alert(data.Movements);
document.body.removeChild(scriptNode);
}
loop = function()
{
scriptNode = document.createElement("script");
scriptNode.type = "text/javascript"
scriptNode.src = "./crashie7.js"; //The crashie7.js file should contained a call to callback() function with json data)
document.body.appendChild(scriptNode);
setTimeout(loop, 700);
}
loop();
////////////Code End
It seems remove a script node while codes in it were executing will cause IE browser crash.
I also found a quick fix for that problem:
////////////Code start
callback = function(data)
{
alert(data.Movements);
// A delay to remove the script node.
setTimeout(function()
{
document.body.removeChild(scriptNode);
}, 3000);
}
////////////Code End
Running above piece on you IE browser and have crash fun!
I don't like to be obligated to open source my server side project of doufu sample game, though it is just a 'prove of concept'.
So rather than GoogleCode, I decided to use Beanstalk (http://www.beanstalkapp.com/) which is a cool svn hoster allow me to use 20mb svn space without charge, and it also allowe me to integrate my project with the other web2.0 service, such as twitter or basecamp.