23.1 Introduction

It would be nice if we could focus our attention on performing only one task at a time and doing it well. That’s usually difficult to do in a complex world in which there’s so much going on at once. This chapter presents C#’s capabilities for developing programs that create and manage multiple tasks. As we’ll demonstrate, this can greatly improve program performance, especially on multicore systems.

Concurrency

When we say that two tasks are operating concurrently, we mean that they’re both making progress at once. Until recently, most computers had only a single processor. Operating systems on such computers execute tasks concurrently by rapidly switching between them, doing a small portion of each before moving on to the next, so that all tasks keep progressing. For example, it’s common for personal computers to compile a program, send a file to a printer, receive electronic mail messages over a network and more, concurrently. Tasks like these that proceed independently of one another are said to execute asynchronously and are referred to as asynchronous tasks.

Parallelism

When we say that two tasks are operating in parallel, we mean that they’re executing simultaneously. In this sense, parallelism is a subset of concurrency. The human body performs a great variety of operations in parallel. Respiration, blood circulation, digestion, thinking and walking, for example, can occur in parallel, as can all the senses—sight, hearing, touch, smell and taste. It’s believed that this parallelism is possible because the human brain is thought to contain billions of “processors.” Today’s multicore computers have multiple processors that can perform tasks in parallel.

Multithreading

C# makes concurrency available to you through the language and APIs. C# programs can have multiple threads of execution, where each thread has its own method-call stack, allowing it to execute concurrently with other threads while sharing with them application-wide resources such as memory and files. This capability is called multithreading.

Performance Tip 23.1

A problem with single-threaded applications that can lead to poor responsiveness is that lengthy activities must complete before others can begin. In a multithreaded application, threads can be distributed across multiple cores (if available) so that multiple tasks execute in parallel and the application can operate more efficiently. Multithreading can also increase performance on single-processor systems—when one thread cannot proceed (because, for example, it’s waiting for the result of an I/O operation), another can use the processor.

Multithreading Is Difficult

People find it difficult to jump between concurrent trains of thought. To see why multithreaded programs can be difficult to write and understand, try the following experiment: Open three books to page 1, and try reading the books concurrently. Read a few words from the first book, then a few from the second, then a few from the third, then loop back and read the next few words from the first book, the second and so on. After this experiment, you’ll appreciate many of the challenges of multithreading—switching between the books, reading briefly, remembering your place in each book, moving the book you’re reading closer so that you can see it and pushing the books you’re not reading aside—and, amid all this chaos, trying to comprehend the content of the books!

Asynchronous Programming, async and await

To take full advantage of multicore architectures you need to write applications that can process tasks asynchronously. Asynchronous programming is a technique for writing apps containing tasks that can execute asynchronously, which can improve app performance and GUI responsiveness in apps with long-running or compute-intensive tasks. Before languages like C#, such apps were implemented with operating-system primitives available only to experienced systems programmers. Then C# and other programming languages began enabling app developers to specify concurrent operations. Initially, these capabilities were complex to use, which led to frequent and subtle bugs.

The async modifier and await operator greatly simplify asynchronous programming, reduce errors and enable your apps to take advantage of the processing power in today’s multicore computers, smartphones and tablets. Many .NET classes for web access, file processing, networking, image processing and more have methods that return Task objects for use with async and await, so you can take advantage of asynchronous programming model. This chapter presents a simple introduction to asynchronous programming with async and await.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset