📄 Page
1
M A N N I N G Nir Dobovizki Asynchronous and multithreaded programming
📄 Page
2
A simple deadlock A classic deadlock: a very common multithreading problem where one thread is holding resource A while waiting for B and a second thread is holding B while waiting for A, resulting in both threads waiting forever. We will talk about deadlocks in chapter 4 and then discuss them in more detail, as well as how to avoid them, in chapter 7. First thread Second thread Lock A Lock B Release B Release A Lock B Lock A Release A Release B waitin g fo rwaiting for Both threads are stuck here.
📄 Page
4
(This page has no text content)
📄 Page
5
MANN I NG Shelter ISland C# Concurrency Nir Dobovizki Asynchronous and multithreaded programming
📄 Page
6
For online information and ordering of this and other Manning books, please visit www.manning.com. The publisher offers discounts on this book when ordered in quantity. For more information, please contact Special Sales Department Manning Publications Co. 20 Baldwin Road PO Box 761 Shelter Island, NY 11964 Email: orders@manning.com © 2025 Manning Publications Co. All rights reserved. No part of this publication may be reproduced, stored in a retrieval system, or transmitted, in any form or by means electronic, mechanical, photocopying, or otherwise, without prior written permission of the publisher. Many of the designations used by manufacturers and sellers to distinguish their products are claimed as trademarks. Where those designations appear in the book, and Manning Publications was aware of a trademark claim, the designations have been printed in initial caps or all caps. Recognizing the importance of preserving what has been written, it is Manning’s policy to have the books we publish printed on acid- free paper, and we exert our best efforts to that end. Recognizing also our responsibility to conserve the resources of our planet, Manning books are printed on paper that is at least 15 percent recycled and processed without the use of elemental chlorine. ∞ Manning Publications Co. 20 Baldwin Road PO Box 761 Shelter Island, NY 11964 ISBN 9781633438651 Printed in the United States of America The author and publisher have made every effort to ensure that the information in this book was correct at press time. The author and publisher do not assume and hereby disclaim any liability to any party for any loss, damage, or disruption caused by errors or omissions, whether such errors or omissions result from negligence, accident, or any other cause, or from any usage of the information herein. Development editor: Doug Rudder Technical editor: Paul Grebenc Review editor: Dunja NikitoviÊ Production editor: Kathy Rossland Copy editor: Lana Todorovic-Arndt Proofreader: Melody Dolab Technical proofreader: Tanya Wilke Typesetter: Tamara ŠveliÊ SabljiÊ Cover designer: Marija Tudor
📄 Page
7
To my wonderful wife, Analia
📄 Page
8
(This page has no text content)
📄 Page
9
vii brief contents Part 1 Asynchronous programming and multithreading ... basics .............................................................................1 1 ■ Asynchronous programming and multithreading 3 2 ■ The compiler rewrites your code 13 3 ■ The async and await keywords 21 4 ■ Multithreading basics 38 5 ■ async/await and multithreading 58 6 ■ When to use async/await 69 7 ■ Classic multithreading pitfalls and how to avoid them 81 Part 2 Advanced uses of async/await and multithreading ....................................................... 103 8 ■ Processing a sequence of items in the background 105 9 ■ Canceling background tasks 122 10 ■ Await your own events 134 11 ■ Controlling on which thread your asynchronous code runs 147 12 ■ Exceptions and async/await 166 13 ■ Thread-safe collections 173 14 ■ Generating collections asynchronously/await foreach and IAsyncEnumerable 203
📄 Page
10
(This page has no text content)
📄 Page
11
ix contents preface xiv acknowledgments xvi about this book xviii about the author xxi about the cover illustration xxii Part 1 Asynchronous programming and ................. multithreading basics .................................1 1 Asynchronous programming and multithreading 3 1.1 What is multithreading? 4 1.2 Introducing multicore CPUs 6 1.3 Asynchronous programming 8 1.4 Using multithreading and asynchronous programming together 11 1.5 Software efficiency and cloud computing 11 2 The compiler rewrites your code 13 2.1 Lambda functions 14 2.2 Yield return 16
📄 Page
12
x contents 3 The async and await keywords 21 3.1 Asynchronous code complexity 22 3.2 Introducing Task and Task<T> 23 Are we there yet? 25 ■ Wake me up when we get there 26 The synchronous option 27 ■ After the task has completed 28 3.3 How does async/await work? 29 3.4 async void methods 33 3.5 ValueTask and ValueTask<T> 35 3.6 What about multithreading? 36 4 Multithreading basics 38 4.1 Different ways to run in another thread 39 Thread.Start 39 ■ The thread pool 42 ■ Task.Run 44 4.2 Accessing the same variables from multiple threads 46 No shared data 48 ■ Immutable shared data 49 Locks and mutexes 49 ■ Deadlocks 50 4.3 Special considerations for native UI apps 52 4.4 Waiting for another thread 52 4.5 Other synchronization methods 53 4.6 Thread settings 54 Thread background status 55 ■ Language and locale 55 COM Apartment 55 ■ Current user 55 ■ Thread priority 56 5 async/await and multithreading 58 5.1 Asynchronous programming and multithreading 59 5.2 Where does code run after await? 62 5.3 Locks and async/await 64 5.4 UI threads 67 6 When to use async/await 69 6.1 Asynchronous benefits on servers 70 6.2 Asynchronous benefits on native client applications 75
📄 Page
13
xicontents 6.3 The downside of async/await 76 Asynchronous programming is contagious 77 ■ Asynchronous programming has more edge cases 78 ■ Multithreading has even more edge cases 79 ■ async/await is expensive 79 6.4 When to use async await 79 7 Classic multithreading pitfalls and how to avoid them 81 7.1 Partial updates 82 7.2 Memory access reordering 85 7.3 Deadlocks 88 7.4 Race conditions 94 7.5 Synchronization 97 7.6 Starvation 99 Part 2 Advanced uses of async/await and .............. multithreading ........................................ 103 8 Processing a sequence of items in the background 105 8.1 Processing items in parallel 106 Processing items in parallel with the Thread class 107 Processing items in parallel with the thread pool 108 Asynchronously processing items in parallel 110 The Parallel class 112 8.2 Processing items sequentially in the background 115 Processing items sequentially in the background with the Thread class 115 ■ The work queue pattern and BlockingCollection 117 ■ Processing important items with persistent queues 119 9 Canceling background tasks 122 9.1 Introducing CancellationToken 122 9.2 Canceling using an exception 130 9.3 Getting a callback when the caller cancels our operation 130 9.4 Implementing timeouts 131 9.5 Combining cancellation methods 132 9.6 Special cancellation tokens 133
📄 Page
14
xii contents 10 Await your own events 134 10.1 Introducing TaskCompletionSource 135 10.2 Choosing where continuations run 139 10.3 Example: Waiting for initialization 140 10.4 Example: Adapting old APIs 141 10.5 Old-style asynchronous operations (BeginXXX, EndXXX) 142 10.6 Example: Asynchronous data structures 143 11 Controlling on which thread your asynchronous code runs 147 11.1 await-threading behavior 148 await in UI threads 148 ■ await in non-UI threads 150 11.2 Synchronization contexts 151 11.3 Breaking away—ConfigureAwait(false) 154 11.4 More ConfigureAwait options 161 11.5 Letting other code run: Task.Yield 162 11.6 Task schedulers 163 12 Exceptions and async/await 166 12.1 Exceptions and asynchronous code 167 12.2 await and AggregateException 170 12.3 The case of the lost exception 171 12.4 Exceptions and async void methods 171 13 Thread-safe collections 173 13.1 The problems with using regular collections 174 13.2 The concurrent collections 178 ConcurrentDictionary<TKey,TValue> 178 BlockingCollection<T> 181 ■ Async alternatives for BlockingCollection 184 ■ ConcurrentQueue<T> and ConcurrentStack<T> 185 ■ ConcurrentBag<T> 186 When to use the concurrent collections 186 ■ When not to use the concurrent collections 186
📄 Page
15
xiiicontents 13.3 The immutable collections 187 How immutable collections work 187 ■ How to use the immutable collections 193 ■ ImmutableInterlocked 194 ImmutableDictionary<TKey,TValue> 195 ImmutableHashSet<T> and ImmutableSortedSet<T> 197 ImmutableList<T> 197 ■ ImmutableQueue<T> and ImmutableStack<T> 197 ■ ImmutableArray<T> 198 When to use the immutable collections 199 13.4 The frozen collections 199 When to use the frozen collections 201 14 Generating collections asynchronously/await foreach and IAsyncEnumerable 203 14.1 Iterating over an asynchronous collection 204 14.2 Generating an asynchronous collection 206 14.3 Canceling an asynchronous collection 209 14.4 Other options 211 14.5 IAsyncEnumerable<T> and LINQ 212 14.6 Example: Iterating over asynchronously retrieved data 212 Example: BlockingCollection<T>-like asynchronous queue 213 index 219
📄 Page
16
xiv preface I’ve been a software developer for over 30 years now and have been developing high-performance servers using multithreading and asynchronous programming since the late 1990s. I’ve been using C# since 2003. For the last decade and a bit, I’ve worked as a consultant, coming into a project for a short period of time and helping solve a specific problem. Over that decade, I’ve had the privilege of visiting many companies, and I’ve gotten to see and help with a lot of projects. While every project is obviously completely different, with each company invent- ing its own innovative, disruptive, and one-of-a-kind technology, after you encounter enough projects, you start to see some similarities. And one thing I’ve seen time and time again are problems arising from incorrect usage of multithreading and asynchro- nous programming. Multithreading is a straightforward concept: it involves running multiple tasks simul- taneously. It is notoriously difficult to get it right, but despite this difficulty, it has been widely used for a long time. Developers like you, who take the time to study multithread- ing through books, are able to use it effectively. Asynchronous programming has existed since the invention of the microprocessor and has long been used in high-performance servers. However, it gained wider popu- larity among average developers when the async/await feature was introduced in C# in 2012. (It was introduced in JavaScript earlier, but in a limited way.) Based on my obser- vations of various projects and my experience conducting job interviews, I’ve found that very few people understand how async/await works. The problems arising from a lack of knowledge in multithreading and asynchronous programming are quite apparent. In just the month or so that I discussed publishing this book with Manning, I taught multithreading and async/await at three different companies.
📄 Page
17
xvpreface And this is how this book was born. What followed was a little more than two years of very deep diving into multithreading and asynchronous programming in C#. During this time, I’ve learned a lot. There is truly no better way to learn something than teach- ing it, and I hope this book will be at least as beneficial to you as writing it was to me.
📄 Page
18
xvi acknowledgments I truly believe this is a very good book, but I didn’t write it alone. Writing a book is a team effort, and it takes an enormous amount of work by many people. Without all those people, this book wouldn’t be as good and, most likely, it wouldn’t exist at all. First, I want to thank my development editor at Manning, Doug Rudder, who had the patience to teach this first-time author how to write a technical book. Associate pub- lisher Mike Stephens, who agreed to publish my idea of a book, helped with support and feedback. Using a food analogy in the first chapter was his idea. And technical edi- tor Paul Grebenc was the first line of defense against technical mistakes. Paul is a Prin- cipal Software Developer at OpenText. He has over 25 years of professional experience in software development, working primarily with C# and Java. His primary interests are systems involving multithreading, asynchronous programming, and networking. Next, I also want to thank all the reviewers who reviewed drafts of this book and everyone who commented while the book was in MEAP: your comments have been invaluable to improving the book. To all the reviewers—Aldo Biondo, Alexandre San- tos Costa, Allan Tabilog, Amrah Umudlu, Andriy Stosyk, Barry Wallis, Chriss Barnard, David Paccoud, Dustin Metzgar, Geert Van Laethem, Jason Down, Jason Hales, Jean- Paul Malherbe, Jeff Shergalis, Jeremy Caney, Jim Welch, Jiří Činčura, Joe Cuevas, Jon- athan Blair, Jort Rodenburg, Jose Antonio Martinez, Julien Pohie, Krishna Chaitanya Anipindi, Marek Petak, Mark Elston, Markus Wolff, Mikkel Arentoft, Milorad Imbra, Oliver Korten, Onofrei George, Sachin Handiekar, Simon Seyag, Stefan Turalski, Sumit Singh, and Vincent Delcoigne—your suggestions helped make this book better. I also want to give my personal thanks to everyone who bought the book while in early access. Seeing that people are interested enough to spend their hard-earned money on
📄 Page
19
xviiacknowledgments a book I wrote is a wonderful feeling, and it was an important part of the motivation to complete the book. And last, but most important, I want to thank my family, and especially my wife, who put up with all my nonsense in general and, in particular, with me spending a lot of our free time in my office writing.
📄 Page
20
xviii about this book This book is designed to help C# developers write safe and efficient multithreaded and asynchronous application code. It focuses on practical techniques and features you are likely to encounter in normal day-to-day software development. It delves into all the details you need to know to write and debug multithreaded and asynchronous code. It leaves out the exotic, fun techniques that are only applicable if you need to build something like your own database server, but that are too compli- cated for normal application code and will probably get you into trouble if you try to use them in normal code, because normal multithreading is difficult enough as it is. Who should read this book This book is for any C# developer who wants to improve their knowledge of multi- threading and asynchronous programming. The information in this book is applicable to any version of .NET, .NET Core, and .NET Framework released since 2012 and to both Windows and Linux (obviously only for .NET Core and .NET 5 and later, since earlier versions do not support Linux). The book focuses more on backend development but also covers what you need to know to write UI applications. How this book is organized: A road map This book has two parts that include 14 chapters. Part 1 covers the basics of multithreading and async/await in C#: ¡ Chapter 1 introduces the concepts and terminology of multithreading and asyn- chronous programming.