Appearance
IO Programming
In computing, IO stands for Input/Output, which refers to the processes of inputting and outputting data. Since programs and runtime data reside in memory and are executed by the CPU, which is a very fast computational core, data exchange typically involves external sources like disks and networks, necessitating IO interfaces.
For example, when you open a browser to access the Sina homepage, the browser needs to use network IO to retrieve the web page. The browser first sends data to the Sina server, informing it that it wants the HTML for the homepage. This action of sending data is called Output. The Sina server then sends the web page back to the browser, which is an action of receiving data and is called Input. Thus, a program typically completes IO operations with two data streams: Input and Output. However, there can be scenarios with only one stream, such as reading a file from the disk into memory (Input) or writing data to a disk file (Output).
Streams in IO
In IO programming, the concept of a Stream is very important. You can think of a stream as a pipe where data flows like water, but it can only flow in one direction. An Input Stream refers to data flowing into memory from external sources (like disks or networks), while an Output Stream refers to data flowing out of memory. For web browsing, at least two streams (or pipes) need to be established between the browser and the Sina server so that data can be sent and received simultaneously.
Synchronous vs. Asynchronous IO
There exists a significant speed mismatch between the CPU and memory versus external devices. For instance, if you want to write 100MB of data to disk, the CPU can output this data in just 0.01 seconds, while the disk may take up to 10 seconds to receive it. What can be done in such cases?
Synchronous IO: The first approach is to have the CPU wait, meaning the program pauses execution of subsequent code until the 100MB of data is written to the disk after 10 seconds. This is called synchronous IO.
Asynchronous IO: The second approach is for the CPU to not wait. It simply tells the disk, "Take your time writing; I’ll continue with other tasks." Subsequent code can then execute immediately. This method is known as asynchronous IO.
The key distinction between synchronous and asynchronous IO is whether the program waits for the IO operation to complete. For example, consider ordering a burger at McDonald's:
Synchronous IO: You say, "I want a burger," and the server replies, "Sorry, the burger needs to be freshly made; it will take 5 minutes." You then stand at the counter waiting for 5 minutes to receive your burger before you can continue shopping.
Asynchronous IO: You say, "I want a burger," and the server tells you, "The burger will take 5 minutes." You can then go shopping first, and they will notify you when your burger is ready. This way, you can engage in other activities while waiting.
Clearly, using asynchronous IO to write programs can significantly improve performance compared to synchronous IO. However, the downside is that the programming model becomes more complex. You need to know when you'll be notified that "your burger is ready," and the methods of notification can vary. If the server finds you in person, that's the callback model; if they text you, you'll need to keep checking your phone, which is polling. In summary, asynchronous IO is much more complex than synchronous IO.
IO Capabilities in Operating Systems
The ability to handle IO operations is provided by the operating system, and every programming language wraps the low-level C interfaces provided by the operating system for ease of use, and Python is no exception. We will discuss Python's IO programming interfaces in detail later.
Note that the IO programming discussed in this chapter will focus on synchronous mode; we will cover asynchronous IO later when discussing server-side programming due to its higher complexity.