Understanding and Avoiding Race Conditions in Multithreaded C# Applications - Pluralsight
Understanding and Avoiding Race Conditions in Multithreaded C# Applications - Pluralsight
Top
Nate Cook Conditions in Multithreaded C#
The Correct Way to Synchronize Access
Applications
Nate Cook
Nov 19, 2018 • 8 Min read • 884 Views
C# Applications
https://www.pluralsight.com/guides/race-conditions-multithreaded-csharp 1/8
7/5/2019 Understanding and Avoiding Race Conditions in Multithreaded C# Applications | Pluralsight
threads,
A Naive Approach to Data Access to perform some work "in the background", as it were. It is
Synchronization
4these cases where race conditions most often appear.
csharp
1 const int MaxLinks = 8000;
2 const int MaxThreadCount = 10;
3 string[] links;
4 int iteration = 0;
https://www.pluralsight.com/guides/race-conditions-multithreaded-csharp 2/8
7/5/2019 Understanding and Avoiding Race Conditions in Multithreaded C# Applications | Pluralsight
5 Synchronization
A Naive Approach to Data Access
4
6 // Start with a single URL (a Wikipedia page, in this exam
7 AddLinksForUrl("https://en.wikipedia.org/wiki/Web_crawler"
8
4
9 while ((links = File.ReadAllLines("links.txt")).Length < M
When to Worry About Race Conditions
10 {
The Case for Synchronizing Access to Data
11 int offset = (iteration * MaxThreadCount);
A Naive Approach to Data Access Synchronization
12
The Correct Way to Synchronize Access
Top 13 var tasks = new List<Task>();
14 for (int i = 0; i < MaxThreadCount && (offset + i) < lin
15 {
16 tasks.Add(Task.Run(() => AddLinksForUrl(links[offset +
17 }
18 Task.WaitAll(tasks.ToArray());
19
20 iteration++;
21 }
csharp
1 static void AddLinksForUrl(string url)
2 {
3 string html = /* retrieve the html for said url */ ;
4 List<string> links = /* extract the links from the html
5
6 using (var fileStream = new FileStream("links.txt",
7 FileMode.OpenOrCreate, FileAccess.ReadWrite, File
8 {
9 List<string> existingLinks = /* read the file contents
10 foreach (var link in links.Except(existingLinks))
11 {
12 fileStream.Write(/* the link URL, as bytes, plus a n
13 }
https://www.pluralsight.com/guides/race-conditions-multithreaded-csharp 3/8
7/5/2019 Understanding and Avoiding Race Conditions in Multithreaded C# Applications | Pluralsight
14 Synchronization
A Naive Approach to Data Access }
4
15 }
When to Worry About Race The key point to note in the main algorithm is that a new thread is
Conditions
being
The Case for Synchronizing initiated
Access with each call to Task.Run . Since we defined a
to Data
A Naive Approach to DataMaxThreadCount
Access Synchronization
of ten, ten threads would be initiated, then
The Correct Way to Synchronize Access
Task.WaitAll would wait until the work in all of those threads
Top
completed. After that, a new batch of threads is initiated in the next
iteration of the while loop.
Fully implemented, this web crawler may actually work fine. But if you
run it enough times, you'll eventually get an IOException . Why is
that?
https://www.pluralsight.com/guides/race-conditions-multithreaded-csharp 5/8
7/5/2019 Understanding and Avoiding Race Conditions in Multithreaded C# Applications | Pluralsight
Actually
A Naive Approach to Data Access yes, that approach may synchronize access to the links file to
Synchronization
4a certain extent. But run it enough times and eventually you will get
another IOException . Essentially, the same problem still exists, but
4 why?
When to Worry About Race Conditions
Remember
The Case for Synchronizing Access tothat
Datawe have multiple threads executing the code in
A Naive Approach to DataAddLinksForUrl
Access Synchronization
simultaneously. The mistake we are making with the
The Correct Way to Synchronize Access
naive approach is that we are not guaranteeing that only a single
Top
thread sets the fileIsInUse flag to true at a time. So, in the moment
that fileIsInUse is set to false in the finally block, multiple
threads may be waiting in the while loop above. If more than one
thread breaks out of the while loop at the same (or almost the
same) time while fileIsInUse is false, they will all enter the try
block, and they will all think they have exclusive access to the file. In
that situation, the IOException will occur. Such an anomaly is an
example of a race condition.
The last thing we learn from the failure of the naive approach is that
"shared data" in the context of multiple threads does not only refer to
files. No, in fact it refers to anything shared across threads, which
https://www.pluralsight.com/guides/race-conditions-multithreaded-csharp 6/8
7/5/2019 Understanding and Avoiding Race Conditions in Multithreaded C# Applications | Pluralsight
includes
A Naive Approach to Data Access variables—be they value types such as the boolean in the
Synchronization
4example above, or reference types.
Test your skills. Learn something new. Get help. Repeat. Start a FREE 10-day trial
https://www.pluralsight.com/guides/race-conditions-multithreaded-csharp 7/8
7/5/2019 Understanding and Avoiding Race Conditions in Multithreaded C# Applications | Pluralsight
4 Professional Services
https://www.pluralsight.com/guides/race-conditions-multithreaded-csharp 8/8