”系统中相对稳定部分“,这个相对稳定部分我想应该指的是方法定义,而不是方法实现,方法实现绝对不可能稳定,试想,有谁写类方法时可以保证它的代码一定不会变呢。但在设计类之前,一般要将方法返回类型、参数等信息定好,这些才是相对稳定的。
至于楼主说的”系统中相对稳定的抽象出来封装成function“, 我感觉好象有一些不妥, 相对稳定用在function是什么意思,还用了封装,封装一般指的是对实现代码的封装,实现代码是不可能相对稳定了,只有方法定义才能称为相对稳定。
实际上,这就是如何处理程序中”做什么“和”怎么做“的问题。
对于”做什么“,实际上就相当于接口中定义的方法(或抽象类中的抽象方法)。注意,不是类中的方法。这是强制的,要求类必须实现这些方法。 因此,接口实际上就是解决“做什么”的问题。而且接口也是多态的核心。多态的核心思想就是只关心方法做什么,而怎么做,完全是由多态后面的实际对象去完成的。也就是说,使用多态只需要面对接口(当然,也可以是类,但一般使用接口来实现多态)。不过方法做什么,用户只面对的是接口中固定的方法,也就是说,方法是一个,可以有多种行文。
关于”怎么做“,当然就是类中的方法实现了。这个就不多说了。
关于面向对象接口、类、抽象类的概念使用代码可能更高地理解他。这种东西通过概念是很难准确理解的。 就象英语语法,没有必要专门去研究,只要多听、多写,多练,总会有明白的一天的。
任何的概念都可能会使初学者理解成别的意思。只要多编写程序才是真正理解它的唯一方法。
3. 封装保证了代码模块化,提高了软件的复用和功能分离
还有这一点,模块化并不是哪种软件设计思想的专利。因为类也可以叫做一个模块,甚至一个子系统都可以叫做一个模块。 将类的封装特性说成是将代码模块化,并没有什么不妥,类也的确是一个模块。 再说,面向对象实际上只是面向过程的升级,在类的内部,就是面向过程和面向对象的混合体。因为在类的内部可以有方法、类全局变量、inner class等。
现在软件设计思想的发展也相当于生物的进化,在进化的过程中也会留下进化链中其他生物的影子。
2、提供稳定的对外接口。因此,系统中相对稳定部分常被抽象成接口。
第二点说的没错,因此后面的是根据“提供稳定的对外接口”引出的,也就是说,一个接口一般会有很多个类实现它(如果只有一个类实现一个接口,那么接口就没有任何意义了),既然是很多类实现它,那么这个接口中的方法就必须是稳定的,如果接口中的方法不稳定,就是说接口中的方法总变的话,那将会影响很多实现它的类。 所以这本书的作者说“将系统中相对稳定部分常被抽象成接口”是没错的,
不过将接口说成是相对稳定的,感觉是有点别扭。实际上,说白了,接口就是所有实现它的类的交集而已。 也就是说,只有很多类拥有同名、同参数的方法,这些方法才可以被抽象出来,形成接口,因此,我个人认为,接口就是类的抽象,而类是对象的抽象。
死锁和线程池的大小关系不大,而是Handler是否被另一只关联等待的线程互相等待。前面有人已经说了。。。
这是我说的。两个线程互相等待。还举了例子,而另外一个人举了另外一个死锁的例子,就是一个线程里调用另一个线程时的死锁。
我的文件服务器是多线程,不是单线程地, 只是线程之间没有关联,如是不会死锁,这好象没错吧。
至于死锁和线程池的关系,有,但不大。它们之间的关系我也是听@Angel Lucifer说的。 主要是线程池会限制线程运行的个数,所以会出现 Angel Lucifer描述的这种情况。
没错,需要设成 ThreadPool.SetMaxThreads(2, 2);methodc才无法调用。
但你的这种产生死锁的方式是通过在线程里启动另外一个线程而造成的,而且在线程中运行另外一个线程,还必须要调用join或EndXXX,必须要满足这两个条件死锁才会发生。 而且这种死锁也非常好查找,但只要不这样做,也就是不在一个线程里运行另一个线程,就算是运行另外一个线程,也不去调用EndXXX或join,这样则可完全避免这种方式的死锁了,最闹心的就是因同步而产生的死锁。这种死锁方式是防不胜防啊。
本文提供的文件下载服务器是绝对不会死锁发生的,顶多由于线程过多,把下载任务给扔了。
能解释这是为什么吗?感觉ThreadPool.SetMaxThreads(1, 1);没起作用。
但这种死种方式只是原因之一,而更复杂的死锁是在运行线程之间的死锁。
再加一层也没有死:
public static void Main()
{
ThreadPool.SetMaxThreads(1, 1);
WaitCallback callback = delegate { MethodA(); };
callback.BeginInvoke(null, null, null);
Console.ReadLine();
}
private static void MethodA()
{
Console.WriteLine("Thread A is beginning");
WaitCallback callback = delegate { MethodB(); };
IAsyncResult result = callback.BeginInvoke(null, null, null);
//模拟实际工作。
Console.WriteLine("1");
Console.WriteLine("2");
Console.WriteLine("3");
//这里永远也不会返回。
callback.EndInvoke(result);
}
private static void MethodB()
{
//永远也不会运行到这里,因为线程池线程耗尽,导致资源竞争引发了死锁。
Console.WriteLine("Thread A is beginning");
WaitCallback callback = delegate { MethodC(); };
IAsyncResult result = callback.BeginInvoke(null, null, null);
//模拟实际工作。
Console.WriteLine("1");
Console.WriteLine("2");
Console.WriteLine("3");
//这里永远也不会返回。
callback.EndInvoke(result);
}
private static void MethodC()
{
Console.WriteLine("methodc");
}
@Angel Lucifer
不好意思,你的这段代码会死锁呢?我在机器上调试,并没有死锁啊,
这种用方式是由于没有线程执行任务导致的死锁,但不知怎么着,在我的机器上没死,会不会是因为双核的缘故。
只有可能发生死锁的任务在不同的线程中同时运行,就可能会发生死锁。 如果这些线程不同时运行,当然就会不发生死锁。
但不知将25增到250可以降低死锁发生的概率是从何说起,怎么将可同时运行的线程数增大就会降低死锁的概率呢?请@ Angel Lucifer 明示
re: 荣获今年7月MVP有感 银河使者 2008-07-18 22:15
SharpDevelop最新的提供了自己的编译器,有空是得深入研究下这个开源地东东。不知SharpDevelop提供的编译器是否是编译C#、vb.net的。不知效率如何?
re: 荣获今年7月MVP有感 银河使者 2008-07-18 22:09
我也非常喜欢编译原理啊,正在研究中,不知楼中想用它来做点什么?
如果下载到501个文件时,页前500个都没下载完,服务器会处理等待状态,在客户端会看到浏览器的进度条正在前进,但不会出现下载对话框,直到有其他的自由线程。或者由于超时而被server抛弃这个任务。
所谓死锁是两个或多个线程互相等待造成的,并不是线程少了就容易死锁,多了就不死了。 还有死锁也是不无法分配线程造成的,那也不叫死锁,所果在线程池中分配不到线程,这个任务就会被放到等待队列中,直接有空闲thread来处理它,或者在超时时间后被丢弃。
死锁其实就是两个或多个线程互相被锁住了。如下面代码(只是伪代码,大概意识)如示:
thread1
{
lock(a)
{
// 假设thread1刚好执行到这被thread2把cpu夺走了
lock(b) { ... }
}
}
thread2
{
lock(b)
{
// 假设thread2刚好执行到这被thread1把cpu夺走了
lock(a) { ... }
}
}
我们可以想象,如果thread1和thread2正好执行到了上面代码注释的部分。那么thread1中要执行lock(b),而thread2中要执行lock(a),但这时a和b都被锁住了,所有就死了。 当然,解决这个问题的方法也非常简单,就是thread1和thread2的lock语句顺序保持一致,代码如下:
thread1
{
lock(a)
{
lock(b) { ... }
}
}
thread2
{
lock(a)
{
lock(b) { ... }
}
}
上面代码是永远不会发生死锁地。
当然不好实现,要是好实现,这个世界的search engine不就满天飞了吗? 再说search engine也不是一个人可以完成的,当然,做做玩还是可以地,了解一下基本的思想。
我想增大工作线程数主要是为了在默认情况下可同时处理更多的工作。至于死锁,是要用同步解决的,和线程池中的线程个数无关。再说ThreadPool最然是500个工作线程,但也是一开始一个一个起的(虽然可以设置),不是一起都运行时。就算是50000个线程也同样会发生dead lock。 因为死锁是相关的一些线程之间协作不好而造成的,和其他无关的线程没有任何关系。
关于你的第二个问题一般排在前面的可以按着url被引用的数目,如url被引用10000次,肯定要在被引用100次的url的前面,还有就是如果url被一个著名网站的首页引用,肯定要比被一个普通网站引用的权值大。然后根据这个权值进行排列。
而且搜索除了检索外,还有一个更难的呢,就是分词啊,我看到你的搜索引擎好象没有分词。 只是利用全文检索到数据库中找数据。
做搜索哪有用数据库地,要自己设计算法(一般不用关系结构,可以使用树型结构,如键树或其他的数据结构),或使用.net开源的搜索框架。
关于webspider我也做了一个:http://www.cnblogs.com/nokiaguy/archive/2008/05/12/1193539.html
但搜索还没做,简单的用framework做一个很容易,练练手,不过做个实用的也比较费劲。
当调用一个线程的join方法时,如果这个线程未执行完,程序将被阻塞。
确定多个线程是否完成的方法很多,但基本可以分为主动式和被动式。主动式就是由每个线程在结束时来通知主线程或其他监视线程。如本文的第一种方法就属性主动式,当然可以将write(...)改成回调函数形式了。
被动方式是由其他的监视线程对多个线程进行监控,本文的第二种方法就属性这种方式。
是不是我blog右边加的那个flash时钟的原因啊,我把它去了看看
是的,直接使用EndInvoke方法,主线程会被阻塞。其实EndInvoke方法相当于Thread.join方法,这个join方法的最大用处就是可以确保多个线程同时结束,再往下执行,EndInvoke的作用和join类似,如果有多个委托使用了BeginInvoke方法,那么可以用这些委托的EndInvoke方法来确保它们都执行完后,再往下进行。如果没这个需求,就要通过回调来使用EndInvoke方法了
确定所有线程是否都完成了工作的方法有很多,如可以采用类似于对象计数器的方法,所谓对象计数器,就是一个对象被引用一次,这个计数器就加1,销毁引用就减1,如果引用数为0,则垃圾搜集器就会对这些引用数为0的对象进行回收。
方法一:线程计数器
线程也可以采用计数器的方法,即为所有需要监视的线程设一个线程计数器,每开始一个线程,在线程的执行方法中为这个计数器加1,如果某个线程结束(在线程执行方法的最后为这个计数器减1),为这个计数器减1。 然后再开始一个线程,按着一定的时间间隔来监视这个计数器,如是棕个计数器为0,说明所有的线程都结束了。当然,也可以不用这个监视线程,而在每一个工作线程的最后(在为计数器减1的代码的后面)来监视这个计数器,也就是说,每一个工作线程在退出之前,还要负责检测这个计数器。
使用这种方法不要忘了同步这个计数器变量啊,否则会产生意想不到的后果。
方法二:使用Thread.join方法
join方法只有在线程结束时才继续执行下面的语句。可以对每一个线程调用它的join方法,但要注意,这个调用要在另一个线程里,而不要在主线程,否则程序会被阻塞的。
个人感觉这种方法比较好。
我在下一讲会详细描述这两种方法的具体实现过程。
上述两种方法都没有线程数的限制(当然,仍然会受到操作系统和硬件资源的限制)
哪位读者有更好的方法也可以跟贴!!
re: LINQ to SQL异步查询 银河使者 2008-07-13 21:38
就象ajax的XMLHttpRequest,向后台发送请求时,最好使用异步方式,要不客户端可能会感觉运行的不太流畅
re: 正确使用异步操作 银河使者 2008-07-13 21:34
SqlCommand command;
IAsyncResult ar = command.BeginExecuteNonQuery();
int result = command.EndExecuteNonQuery(ar);
这段代码和没使用异步一样了,应该使用回调方式。
如果程序中需要大量的线程,还是使用Thread来处理吧。BeginInvoke方法并不是用来执行大量密集操作的,我个人感觉这个方法只是为了程序中的某个按钮或其他的控件触发某个事件时,使用这个方法来执行一下异步操作。但这个异步操作的时间可能很短(可能只有几秒甚至更短)。如果使用了BeginInvoke来执行这个操作,程序就不会有停顿的感觉。这有点象AJAX中的XMLHttpRequest的异步请求,只是为了使客户端不会有停顿的感觉。以便增加用户体验。
事实上,真正的线程应用是Thread,这才是.net线程的核心。
第一篇文章很简单,以后会逐渐增加一些难度。循序见进。
是的,EndXXX方法和join方法类似,因此,本文所述的方法除了回调外,其他的都是阻塞的,只是四、五所用的方法在EndXXX返回前给用户一些动作显示,这样用户就知道程序没有死,还在正常运行。
文章没用啥,只有两个图,是不是浏览器的问题啊,有可能中招了。
第二种方法没法建视图,因为这个SQL是动态生成的,长度也不定,里面的子查询也不定。 由于是自动生成的,因此,我们不必关心它的复杂度。
create function好象做不了聚合函数吧!
我的vs2008在windows xp sp3下的asp.net设计视图不好使了,一点设计就不动了,谁知道是怎么回事。 在vista下就好使。 不知这算不算bug。 我下了个这方面的patch也不好使。 郁闷。 有没有人遇到和我一样的问题??
re: 查询功能的开发 银河使者 2008-06-15 19:38
对于分页的功能,目前新版本的数据库,如sql server2005、mysql都提供了支持。至于老版本的数据库,可能需要做进一步的处理。
分页也是web的一个局限性,记得以前使用C/S模式开发程序时,由于有缓冲,不管多少条记录,都可以一下子显示(虽然从底层并不是一起出来的,但这些都是由系统做的,一般并不需要开发人员去关心)。
public static Dictionary<string, DbParameter> BaseParameter = new Dictionary<string, DbParameter>();
BaseParameter.Add("1", new System.Data.SqlClient.SqlParameter());
不知道第2句怎么写到这了,是应该写在静态构造方法中吧,还是...
如果是静态的成员,在第一次访问类时只执行一次。当然是获得同一个对象实例了。
得自己先建一个excel文件,好象是系统无法创建新的excel文件。
@Sangplus
这是扩展方法的重要应用之一。在没有源代码的前提下,在第三方的类中加入方法,使其看上去看这个类本身的方法一样。 这个实现起来很简单。
re: 可以使用C#语言的在线ACM题库 银河使者 2008-06-11 12:10
不是一个人。
是的,推测早就有了。推测能力的强弱也标志着编译器是否更智能化。
是的,编译器一直在玩魔术。不光是.net,java也是一样,现在为了保持二进制的兼容性,都不在底层做文章了,而是在语法上做一些手脚。 就象foreach语句,就是这样。 不过反编译一下,就会原形毕露了。
现在编译器除了编译正常的语法,还要做一些转换工作,也够累的。不过还好,没有要求加薪。
C#3.0现在才刚刚在国内兴起,还有走很长时间。总是有很多初学者在学习它。对于初学者来说,永远不会过时的。但对于.net专家来说,这些东西太简单了。完全没有必要去看。
本文并不是面向所有读者,而只是针对那么并没有掌握这些技术的读者。
@(笑煞天)
当然,只要没指定类型名的,都是匿名类型
不管是糖,还是ice cream,都是我喜欢的。 微软这么做也是有好处地,可以保持IL的兼容性。
就象java和byte code,也提供了不少sugar。 看来这是未来语言的趋势啊。
还有,本文也已指出,应尽量避免使用var。 本文所讲的技术在很大程度上是为了linq而出现的。
@路过
我的以前开头的一段也许并不能说是错误,可能是有措辞上的问题。有一些歧义。另外声明一下,我可从来没说过var是弱类型,或是可以改变类型。 那句“为一个变量赋不同类型的值(如int和string)”,我的原意是在初始化时可以为其赋不同类型的值,并没有再次赋不同类型值的意思。 但这句确实有一些歧义,现在去掉了。大家看看还有哪句有歧义,尽管指出。