DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workkloads.

Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • Zero to AI Hero, Part 3: Unleashing the Power of Agents in Semantic Kernel
  • Zero to AI Hero, Part 2: Understanding Plugins in Semantic Kernel, A Deep Dive With Examples
  • From Zero to AI Hero, Part 1: Jumpstart Your Journey With Semantic Kernel
  • Mastering Concurrency: An In-Depth Guide to Java's ExecutorService

Trending

  • Integrating Security as Code: A Necessity for DevSecOps
  • Medallion Architecture: Why You Need It and How To Implement It With ClickHouse
  • Event-Driven Architectures: Designing Scalable and Resilient Cloud Solutions
  • Unlocking the Potential of Apache Iceberg: A Comprehensive Analysis
  1. DZone
  2. Coding
  3. Languages
  4. Tasks, BackgroundWorkers, and Threads

Tasks, BackgroundWorkers, and Threads

An introductory view to three tools used for concurrency in .NET.

By 
Nick Cosentino user avatar
Nick Cosentino
·
Updated Feb. 15, 23 · Tutorial
Likes (1)
Comment
Save
Tweet
Share
1.1K Views

Join the DZone community and get the full member experience.

Join For Free

Even if you’re new to C#, you’ve probably come across at least one Task, Thread, or BackgroundWorker. With a bit of additional time, it’s likely you’ve seen all three in your journey. They’re all ways to run concurrent code in C# and each has its own set of pros and cons. In this article, we will explore how each one operates at a high level. It’s worth noting that in most modern .NET applications and libraries, you’ll see things converging to Tasks.

The Approach

I’ve gone ahead and created a test application that you can find here. Because this is in source control, it’s possible/likely that it will diverge from what we see in this article, so I just wanted to offer that as a disclaimer for you as the reader.

The application allows us to select different examples to run. I’ll start by pasting that code below so you can see how things work.

using System.Globalization;

internal sealed class Program
{
    private static readonly IReadOnlyDictionary<int, IExample> _examples =
        new Dictionary<int, IExample>()
        {
            [1] = new NonBackgroundThreadExample(),
            [2] = new BackgroundThreadExample(),
            [3] = new BackgroundWorkerExample(),
            [4] = new SimultaneousExample(),
        };
    private static void Main(string[] args)
    {
        Console.WriteLine("Enter the number for one of the following examples to run:");
        foreach (var entry in _examples)
        {
            Console.WriteLine("----");
            var restoreColor = Console.ForegroundColor;
            Console.ForegroundColor = ConsoleColor.Cyan;
            Console.WriteLine($"Choice: {entry.Key}");
            Console.ForegroundColor = ConsoleColor.Magenta;
            Console.WriteLine($"Name: {entry.Value.Name}");
            Console.ForegroundColor = ConsoleColor.Yellow;
            Console.WriteLine($"Description: {entry.Value.Description}");
            Console.ForegroundColor = restoreColor;
        }
        Console.WriteLine("----");
        IExample example;
        while (true)
        {
            var input = Console.ReadLine();
            if (string.IsNullOrWhiteSpace(input))
            {
                Console.WriteLine("Would you like to exit? Y/N");
                input = Console.ReadLine();
                if ("y".Equals(input, StringComparison.OrdinalIgnoreCase))
                {
                    return;
                }
                Console.WriteLine("Please make another selection.");
                continue;
            }
            if (!int.TryParse(input, NumberStyles.Integer, CultureInfo.InvariantCulture, out var exampleId) ||
                !_examples.TryGetValue(exampleId, out example))
            {
                Console.WriteLine("Invalid input. Please make another selection.");
                continue;
            }
            break;
        }
        Console.WriteLine($"Starting example '{example.Name}'...");
        Console.WriteLine("-- Before entering example method");
        example.ExecuteExample();
        Console.WriteLine("-- After leaving example method");
    }
}

Threads

Threads are the most basic form of concurrent execution in C#. They are created and managed by the operating system, and can be used to run code in parallel with the main thread of execution. The concept of a thread is one of the most basic building blocks when we talk about concurrency in general for programming. However, it’s also a name of a class that we can directly use in C# for running concurrent code.

Threads allow you to pass in a method to execute. They also can be marked as background or not, where a background thread will be killed off when the application attempts to exit. Conversely, a non-background thread will try to keep the application alive until the thread exits.

Here is an example of creating and starting a new thread:

Thread newThread = new Thread(new ThreadStart(MyMethod));
newThread.Start();

One major advantage of using Threads is that they have a low overhead, as they are managed directly by the operating system. However, they can be more difficult to work with than other concurrent options, as they do not have built-in support for cancellation, progress reporting, or exception handling. In C#, we’ve had access to the Thread object for a long time so it makes sense that other constructs have been built on top of this for us adding additional quality of life enhancements.

Let’s check out the first Thread example:

    public void ExecuteExample()
    {
        void DoWork(string label)
        {
            while (true)
            {
                Task.Delay(1000).Wait();
                Console.WriteLine($"Waiting in '{label}'...");
            }
        };

        var thread = new Thread(new ThreadStart(() => DoWork("thread")));
        thread.Start();

        Console.WriteLine("Press enter to exit!");
        Console.ReadLine();
    }

In the context of our sample application, we would be able to see the method printing to the console while the Thread is running. However, when the user presses enter, the example method would exit and then the program would also try to exit. Because this Thread is not marked as background, it will actually prevent the application from terminating naturally! Try it out and see.

We can directly compare this with the second example, which has one difference: The Thread is marked as background. When you try this example out, you’ll notice that the running thread does not prevent the application from exiting!

Background Workers

BackgroundWorker is a higher-level concurrent execution option in C#. It is a component included in the System.ComponentModel namespace, and generally you see this used in GUI applications. For example, classic WinForms applications would take advantage of these.

Let’s look at our example for BackgroundWorker:

    public void ExecuteExample()
    {
        void DoWork(string label)
        {
            while (true)
            {
                Task.Delay(1000).Wait();
                Console.WriteLine($"Waiting in '{label}'...");
            }
        };

        var backgroundWorker = new BackgroundWorker();
        // NOTE: RunWorkerCompleted may not have a chance to run before the application exits
        backgroundWorker.RunWorkerCompleted += (s, e) => Console.WriteLine("Background worker finished.");
        backgroundWorker.DoWork += (s, e) => DoWork("background worker");
        backgroundWorker.RunWorkerAsync();

        Console.WriteLine("Press enter to exit!");
        Console.ReadLine();
    }

One major advantage of using a BackgroundWorker is that it has built-in support for cancellation, progress reporting, and exception handling. It is setup slightly different in that event handlers are registered onto the BackgroundWorker. You can additionally have a completion handler and others registered to the object. Like a Thread marked as background, the BackgroundWorker will not block the application from exiting.

Tasks

Want to see how Tasks factor in? Be sure to check out my blog post that details Tasks in the same way. It summarizes with a conclusion about how you may want to use each in your programs!

application Task (computing) C# (programming language) .NET

Published at DZone with permission of Nick Cosentino. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Zero to AI Hero, Part 3: Unleashing the Power of Agents in Semantic Kernel
  • Zero to AI Hero, Part 2: Understanding Plugins in Semantic Kernel, A Deep Dive With Examples
  • From Zero to AI Hero, Part 1: Jumpstart Your Journey With Semantic Kernel
  • Mastering Concurrency: An In-Depth Guide to Java's ExecutorService

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!