控制台类似于socket,是由两个流组成的一个复合对象。“由两个流组成的复合对象”初看起来有点奇怪,仔细研究发现计算机中凡是实现双向通信的装置大都如此。流是单向的,是一端读取另一端的数据,或者说是另一端向一端写入数据。这只能实现单向的通信,成为单工。如果有了正反两个方向的流,就构成了一个双向通信的装置,成为双工。并且这个装置可以实现数据同时在两个方向上流动。
还有一种通信通道被称为“半双工”,虽然也可以实现双向通信,但是只能是由双方分时复用一条通信线路,即不能同时。我觉得计算机在底层实现半双工反而比双工困难。在高层应用可以找到一些半双工模式的通信,例如HTTP。这些模式好像在底层都是由双工实现的,只是放弃了同时双向通信的特质。
控制台的Write方法是原子性的,不会被其他线程所中断。我认为控制台的这一特性为使用控制台进行多线程测试奠定了基础。这一点的不到保证的话控制台在多线程下的输出将十分可怕。
当线程执行到Read系列方法的时候,如果流中有字符,将读取出来,并将该字符echo到控制台的输出流;如果读取完毕则返回,如果没有读取完毕就继续读去。如果读取时输入流中没有任何数据,那么线程将被阻塞。这就是为什么我们输入字符的时候,这些字符会被回显到显示器上。Echo写只是普通的对控制台的写,如果此时控制台正在执行大量的Write方法调用,那么Echo字符将与这些调用以未指定的方式夹杂在一起。
这里一个令人困惑的地方是,如果程序已经阻塞,为什么控制台还能接受按键呢?我认为,控制台是操作系统中的一个装置,是由操作系统维护的,而我们的程序只是和这个装置打交道而已。虽然我们的程序阻塞了,但是操作系统是能够正常响应的。因此当用户有了特定的输入是,操作系统向这个控制台的输入流写入数据,从而解除了我们程序的阻塞状态。这也是为什么我们的程序在不断的输出过程中(while(true)Console.WriteLine(“xxx”);)能够响应break键的操作。控制台的输入流和输出流都是独占性资源,必须获取其锁才能访问。而当按下break键,操作系统将尽快回收控制台的锁,从而使程序中的Write方法阻塞。
同样可知,Console.Read系列方法也是要独占控制台的输入流才能执行的。因此多个线程的Console.Read方法同时执行,一次只有一个线程能够获得输入。例如如果是ReadLine方法的话,将只有一个线程能够从控制台读取一行数据。
Test: