-
Table of Contents
- .NET Book Recommendations
- Getting Started with .NET
- The Microsoft .NET Framework
- The Common Language Runtime (CLR), the Common Type System (CTS), and the Common Language Specification (CLS)
- .NET Framework Class Library
- Visual Studio .NET
- .NET Enterprise Servers and .NET My Services
- .NET Compliant Languages
- C#
- Visual Basic .NET (VB .NET)
- ASP.NET
- XML Web Services
- ADO.NET
- XML.NET
- Windows Forms
- Why .NET?
- Displaying Errors with the Error Provider
- COM Interoperability
- Comparing Java and .NET
- Calling Unmanaged Code
- .NET Application Security
- Code Access Security
- .NET Standards Support
- Numeric Types in the .NET Framework
- Working with Strings
- Formatting Strings
- Trimming Character Strings
- Comparing Strings in .NET 2.0
- Arrays and Collections
- Arrays as Class Members
- Sorting a Multi-Dimensional Array
- Sorting a Multi-Dimensional Array with LINQ
- File I/O (System.IO)
- Working with File Names
- Using the File System
- Working with Files and Directories
- Monitoring the File System
- Working with Streams
- Working with Text Encodings
- Working with Date and Time
- Extending the DateTime Class
- Using DateTimeOffset
- Fun with Dates
- Exceptions
- Delegates
- Events
- Asynchronous Programming
- Asynchronous File I/O
- Timers
- Random Numbers
- Cryptographically Secure Random Numbers
- Serialization
- MultiThreading (System.Threading)
- Multi-Threading Overview
- The Managed Thread Pool
- Managed Threading
- Thread Synchronization
- Synchronizing Data Access
- Trace Debugging
- Tracing in .NET 2.0
- ASP.NET Trace
- Validating User Input in ASP.NET Web Pages
- Event Logging
- Monitoring Application Performance
- Accessing the Registry
- Accessing Environment Information
- Environment Variables in .NET 2.0
- Managing Windows Forms Applications
- Working with Email
- Working with Graphics
- Animating a Background
- Working with Images
- Drawing Cycloid Curves
- Simulating the Spirograph
- Building International Web Applications
- .NET Compact Framework
- Mobile Web Development with ASP.NET
- Speech Technologies
- Microsoft MapPoint Web Service
- Working with Typed DataSets
- Using Relationships in DataSets
- DataColumn Expressions
- Playing Simple Sounds
- Playing Sounds with .NET 2.0
- Returning an Image in a Web Page
- RSS
- Best Practices — Project Structure
- Best Practices — Application Blocks
- The Data Access Application Block
- The Exception Management Application Block
- Best Practices — Performance
- Best Practices — Performance and Scalability
- Best Practices - Testing
- Reading the Tea Leaves, 2005
- Predictions: A Look Back at 2005, and a Look Ahead to 2006
- .NET Downloads
- Application Deployment Overview
- Application Deployment — Versioning
- Application Deployment — Version Policy
- Application Deployment — Packaging and Distribution
- .NET Remoting Overview
- A Remoting Demonstration
- Remoting Configuration
- Remoting: Lifetimes and Leases
- Remoting: Other Issues
- Attributes
- Writing Custom Attributes
- Accessing Attributes in Code
- Reflection
- Class Design: Inheritance, Interface, or Composition?
- The TriTryst Game
- Console Applications in .NET 2.0
- New File I/O Methods in .NET 2.0
- Building Projects with MSBuild
- Unmanaged Callbacks in .NET 2.0
- Timer Troubles
- Non-Rectangular Windows Forms
- Windows Forms Transparency
- 10 Things I Hate About Visual Basic
- 10 Things I Hate About C#
- Background Processing with Idle Time
- Scaling Windows Forms
- Reading and Writing Binary Data
- New Memory Management Functions in .NET 2.0
- Compatibility Between .NET 1.1 and .NET 2.0
- Managed Debugging Assistants in .NET 2.0
- XDir: A Program for Viewing Directory Sizes
- The Microsoft.VisualBasic Namespace
- Operator Overloading
- Working with GPS Data
- Hidden Visual Studio Tools
- .NET 3.0
- The .NET 2.0 Stopwatch Class
- Nullable Types
- Drawing Rotated Text
- Unsafe Code
- Other .NET Languages
- Compiler Directives
- Safe Handles
- Predictions, 2007 Edition
- New Features in C# 3.0
- Generics
- Network Client Programming
- On the Misuse of Exceptions
- Maximum Object Size in .NET
- More on Maximum Object Sizes
- Keyed Collection Memory Limitations
- Matching String Endings
- Allocating Small Data Structures
- Grumbling About Limitations
- Some Thoughts on the Nature of What We Do
- Working with Predicates in Collections
- Working with DataReaders
- Outputting XML with XmlWriter
- Writing XML Data
- Working with Compression
- Another Look at Compressed Streams
- Compressing a Very Large File
- Canonical URIs
- Constructing URIs
- Using OneWayAttribute for Remote Calls
- Selecting a Garbage Collector
- Linked List
- Linked List Application - The MRU List
- Auto-implemented Properties in C#
- The HashSet Collection
- Looking Ahead: 2018
- An Experiment in Optimization
- A Larger Integer
- Extension Methods
- Language Integrated Query (LINQ)
- Variable Length Parameter Lists
- The ReaderWriterLockSlim Synchronization Primitive
- Sorting a Text File
- Sorting a Large Text File
- Using ListView with Large Data Sets
- LINQ One-Liners
- Regular Expression Optimization
- Random File I/O
- Computing the Size of a Structure
- More on Computing Structure Sizes
- UnmanagedMemoryStream
- Dynamically Loading Code
- Building a String Table
- Delegates Versus Function Pointers
- Visual Studio Editor Features
- A Simple Profile Timer
- New Features in C# 4.0
- IEnumerator or IList?
- New Features in .NET 4.0
- Set Operations with IEnumerable and HashSet
- Using File Locks
- Extending Object Functionality
- Clearing a HashSet
- When Hash Codes Matter
- Parsing Command Line Options
- Creating a Single-Instance Program
- Asynchronous Windows Forms Events
- The BackgroundWorker Component
- Fixing a Dumb Mistake
- Thinking About Multi-Threaded Programs
- JavaScript Object Notation
- Better JSON Processing with JSON.Net
- Useful .NET-related Sites
- Markov Models
- Building an Order 0 Markov Model
- Higher Order Markov Models
- Webmaster's Guide to robots.txt
- An Overview of the Parallel Extensions to .NET
- Parallel Extensions Synchronization Objects
- Thread Safe Collections
- A Bug and a Conundrum
- Another Bug and an Answer
- Task Parallel Library
- Good and Bad Ideas in C#
- Parallel LINQ
- Copying Large Files
- Replacing File.Copy
- Learning from Our Mistakes
- Symbolic Links
- There Is No Easy Fix
- Tracking Hurricanes
- Examining Hurricane Data
- Searching for Multiple Strings
- Simple JSON Processing
- Aho-Corasick String Searching
- Writing a Web Crawler
- Web Crawler Politeness
- Source Control Management
- Subversion
- Communicating with Datagrams
- Fun with Actions and Funcs
- The Future of Media
- The Importance of Metadata
- Of Comparison and IComparer
- IComparer, Comparer, IComparable, Oh My!
- Comparing Generic Types
- A Simple HTTP Server
- Quantizing DateTime Fields
- More Fun with the Garbage Collector
- Refactor, Don't Rewrite
- A Generic BinaryHeap Class
- A Generic File Sorter
- Birthdays, Random Numbers, and Hash Keys
- Random Selection from Large Groups
- Command Line Tools for Windows
- Reading and Writing, Bit by Bit
- Selecting the Top N Items from a Group
- Determining Website Content Encoding
- Benefits and Drawbacks of Syndication
- Pubsubhubbub
- Memory Use Misconceptions
- Risk, Lost Opportunity, and Other Hidden Upgrade Costs
- Culture Shock: from .NET to JavaScript
- Using .NET for a Startup
- Tracking Wikipedia Changes with IRC
- Browser Applications and the Same Origin Policy
- Handling the Unexpected
- Dealing with Growth
- Deleting the Oldest File
- Where Do I Put Stuff?
- .NET Timer Resolution
- Exploring Options for Better Timers
- Using the Windows Timer Queue API
- Locks Aren't Slow
- Alternatives to Locks
- Lock Free Concurrent Collections
- The BlockingCollection Class
- Customizing BlockingCollection
- What Time Is It? Daylight Saving Time and Computers
- Using enums to Save Memory
- New File Operations in .NET 4.0
- Building a Hierarchy of Rectangles
- A Faster File Copy
- Constants Are Forever
- The Dangers of Floating Point
- Goto is Not Inherently Evil
- The Weakest Link
- Reducing Memory Required for Strings
- Grouping with LINQ
- HttpListener "Gotchas"
- Extension Methods Are Evil
- Finding the Registered Domain in a URL
- Drawing Text
- Obfuscating Sequential Keys
- Properties of Obfuscated Keys
- Finding Changes Between Two Lists
- Using the ConcurrentBag Collection
- Never Sleep!
- Shuffling and Sorting
- Viewing Large Text Files
- Use the Right Tool
- Why GetHashCode Matters
- Optimization Guidelines
- Timer Differences
- The Mutex
- Modifying a Working System
- Building a New Type of Stream
- More Large File Problems
- A Better File.Copy Replacement
- Throwing the Wrong Exception
- Approximate Counters
- Monitoring a Timer
- Combining Consoles and Forms
- Embedding a Text Resource
- Handling Concurrent Downloads
- The Importance of Domain Knowledge
- Stupid Programmer Tricks
- Aho-Corasick Revisited
- Expressiveness is the Soul of Brevity
- Fun with Anonymous Types
- Simplifying a Multi-Threaded Application
- Work Smarter
- The Skip List Data Structure
- A More Memory-Efficient Skip List
- Selection Revisited
- Why Async?
- What the Future Holds
- The "Roslyn" CTP
- Where We've Been
- Informit Reference Library
The BlockingCollection Class
Last updated Mar 14, 2003.
At first look, the new BlockingCollection looks useful, but not hugely important. But after a bit of study, you begin to appreciate all it can do. The MSDN documentation says that BlockingCollection "Provides blocking and bounding capabilities for thread-safe collections that implement IProducerConsumerCollection<T>." Let's take a look first at the blocking and bounding capabilities. Then we'll look at where the IProducerConsumerCollection interface comes in.
You can use BlockingCollection in two ways: with or without a backing store. If you don't specify a backing store, then BlockingCollection uses a ConcurrentQueue for the backing store, and the resulting collection behaves much like a ConcurrentQueue with a slightly different interface. Those differences, though, are very important.
The "bounded" part of the description is simple: you can set an upper limit on the number of items to be stored in the collection. This is very important if you're processing more data than will fit in memory. For example, I have a program that on a daily basis merges about 100 million transactions into an existing repository. The main processing loop for that program can be described like this:
while not end of repository
read next repository record
read all transactions for this record
add transactions to record
write modified repository record
end while
Rather than making the main loop wait on file input when reading the transactions, I have a separate thread reading from the disk and placing transactions into a queue so that the main loop gets transactions from the queue very quickly. Doing it this way almost doubles the program's speed. I do the same kind of thing when reading records from the repository and writing modified records to the new repository.
The thread that reads transactions from disk can fill the queue a whole lot faster than the main processing loop can empty it, and if I let it go it would fill memory with transactions and then the program would crash with an out of memory exception. Such a situation is commonly referred to as "unbounded memory usage."
To prevent unbounded memory usage, BlockingCollection lets you set the maximum number of items to be held in the collection. An attempt to add another item will block until there is space in the collection to hold it. BlockingCollection takes care of the complicated code required for waiting and periodically polling to see if there is space for the item.
You set the upper limit by specifying it in the constructor. So if you want a BlockingCollection that can hold at most 100,000 items, you would write:
BlockingCollection<MyType> = new BlockingCollection<MyType>(100000);
To determine the bounded capacity of an existing collection, query the BoundedCapacity property. If there is no bound on the capacity, BoundedCapacity will return int.MaxValue.
Adding items to a BlockingCollection
To add an item to a BlockingCollection, you call the Add method, or one of the overloads of the TryAdd method. Add adds the item to the collection. If the collection is already at capacity, Add blocks until the item can be added.
There are several overloads of TryAdd. TryAdd(T) tries to add the item and returns immediately. The return value is True if the item was added, or False if the collection was full. TryAdd(T, time) will try to add the item within the specified time period. So TryAdd(t, 1000) will try multiple times within a one-second period, until either the item is added successfully or the interval expires. TryAdd(T, Timeout.Infinite) is equivalent to calling Add(T). It will wait indefinitely.
When you're finished adding items to the collection, you can call CompleteAdding to indicate that no more items will be added. Marking the collection as complete for adding prevents any other items from being added (attempts will result in an InvalidOperationException), and calls to remove items will not block if the collection is empty.
Removing items from a BlockingCollection
The method to remove an item from a BlockingCollection is Take. Take returns the next item from the collection. If there are no items in the collection, Take will block until an item is added. If the collection has been marked as complete for adding and there are no items, then Take throws InvalidOperationException. Calling Take probably isn't a good idea if there are multiple threads removing items from the collection, because it's impossible to prevent a situation in which Take is called to remove an item from an empty collection.
A better option is the TryTake method with an infinite timeout, which will return False if an item cannot be removed--even if the collection has been marked as complete for adding. The code below shows the difference
BlockingCollection<int> b = new BlockingCollection<int>(10); b.CompleteAdding(); int i; bool rslt = b.TryTake(out i, Timeout.Infinite); // rslt will be false i = b.Take(); // throws InvalidOperationException
There is also a TryTake method that does not wait at all. If you want to get an item if there's one ready right now, but not wait for one, you could write:
int i;
if (b.TryTake(out i))
{
// got item
}
Of course, most programs that process things from a collection do so in a loop, continuing until there are no more items to process. BlockingCollection makes that very easy. If your processing loop just waits for the next item, processes it, and then repeats, the loop couldn't be simpler:
int i;
while (b.TryTake(out i, Timeout.Infinite))
{
// process item
}
// Collection is empty
If you want your program to do other things when there's no item available in the collection, you have to call TryTake with a smaller timeout value, and check the IsCompleted property periodically so you know when to quit.
while (!b.IsCompleted)
{
int i;
if (b.TryTake(out i, TimeoutValue))
{
// Process item
}
else
{
// do something else in lieu of processing an item
}
}
// Collection is empty
As I said before, instantiating a BlockingCollection without specifying a backing store will create a collection that uses a ConcurrentQueue as the backing store. Using BlockingCollection in that way gives you a very good producer/consumer FIFO queue. Producers add items to the collection by calling Add or TryAdd, and the BlockingCollection makes sure that the queue doesn't grow too large. Consumers can call TryTake to remove items, confident that the items will be removed in the order in which they were added, and not having to worry about polling or handling special cases. All the complicated blocking and polling code is handled by BlockingCollection, and synchronization to prevent data structure corruption is handled by the underlying ConcurrentQueue.
It's very likely that if you're using ConcurrentQueue in your programs, you would be better off using BlockingCollection. It will almost certainly simplify your code.
