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
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
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
Partner Zones AWS Cloud
by AWS Developer Relations
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
Partner Zones
AWS Cloud
by AWS Developer Relations

Parallelizing Windows Applications in .NET 4.0

Tanzim Saqib user avatar by
Tanzim Saqib
·
May. 06, 10 · Interview
Like (0)
Save
Tweet
Share
18.05K Views

Join the DZone community and get the full member experience.

Join For Free

Task Parallel Library (TPL) is designed to take advantage of multicore processors in user code easily. It is shipped with Visual Studio 2010 as part of .NET Framework 4.0. It was previously available as Parallel Extensions which could be used with Visual Studio 2008 applications, too. TPL gives you the convenience to turn existing sequential code into parallel branches, which may result into significant performance boost since all the available processors are being utilized. Although ThreadPool.QueueUserWorkItem is still there and can serve you the same as before, TPL is a more organized library which has richer APIs that support waiting, continuations, better exception handling, and so on. It is available at System.Threading.Tasks namespace.

Parallel Loops

Parallel.ForEach is probably the simplest form of parallelism. It may iterate through an IEnumerable in parallel fashion. However, whether it is parallel or not, it will call an Action with the current collection item as a parameter. Here is an example:

var magicString = string.Empty;

var list = new List<string> {".NET", "Zone"};
Parallel.ForEach(list, oneString =>
{
magicString += oneString;
});

Remember, parallelism is not guaranteed. TPL contains sophisticated algorithms which automatically adapts to the workload according to the machine configuration. However, whether you have a single processor or not, you can safely use TPL as it was built for running on such processors as well. From the given example, it is quite clear that parallelism we wanted to achieve for just a simple List<string> is overkill. Therefore, it is always wise to use when there is a necessity or chance to obtain improvement when run in parallel. 

Parallelizing Tasks

TPL made task parallelization super convenient. It offers Task approach of parallelism, instead of Thread. Task wraps Thread and provide with more meaningful and necessary methods. You could obviously write the following code, spawn a thread into thread pool and achieve parallelism in previous versions of .NET: 

static void Main()
{
ThreadPool.QueueUserWorkItem(new WaitCallback(DoWork));
}

private void DoWork(object info)
{
}

Using TPL, it can be written like below:

static void Main()
{
Task.Factory.StartNew(() => DoWork());
}

private void DoWork()
{
}

This basically does the same, queues a thread to the worker thread pool, but in more convenient way. In order to obtain a reference to the task and play with it, we could assign it to a variable.

var task = Task.Factory.StartNew(() => DoWork());

 The Task class has quite a few properties and methods to make our life in parallel world real easy. If reference is not all that necessary, we could use Parallel.Invoke, too. This method takes one or more Actions, executes them in parallel.

Parallel.Invoke(() => DoFirstWork(), () => DoSecondWork(), () => DoNWork());

You can also chain one parallel task to another using ContinueWith.

Task.Factory.StartNew(() => DoFirstWork()).ContinueWith((t) => DoSecondWork()).ContinueWith((t) => DoNWork());

Main advantage of Task over Thread approach is, as I have mentioned before, TPL contains very sophisticated algorithms which adapts to workload and it has its own calculation to maximize resource utilization to achieve the best performance possible. So, we are off the hook from creating and managing threads by ourselves and pondering whether it is going to be fruitful for our particular needs or not.

Parameterized Tasks

Thread management in earlier versions of Visual Studio is huge pain. You needed to keep track of the threads’ statuses almost manually. You had to pass parameters as object. No strongly typed objects could be passed, so type castings were in places. Here’s an example:

static class Program
{
private static int output = 0;
private static ManualResetEvent resetEvent = new ManualResetEvent(false);

private static void DoWork(object info)
{
output = (int)info + 100;
resetEvent.Set();
}

static void Main()
{
ThreadPool.QueueUserWorkItem(new WaitCallback(DoWork), 20);
WaitHandle.WaitAll(new WaitHandle[] { resetEvent });
Console.WriteLine(output);
}
}

The same can be achieved by the following using TPL. See how easy and more C#-ish it looks like:

static class Program
{
private static int DoWork(int info)
{
return info + 100;
}

static void Main()
{
var task = Task<int>.Factory.StartNew(() => DoWork(20));
task.Wait();
Console.WriteLine(task.Result);
}
}

The Task class takes the type of return value while calling the delegate. Then we have waited until the thread is done and returned the value. The result can be found by task.Result.

Updating UI Has Never Been So Elegant

Responsive UI is one of the most important attributes of a good application. Blocking UI thread for a background operation to complete, is a very bad practice, should be avoided at any cost and may result in Not Responding application. There is no special offering from TPL to make it easier, but its elegant style of handling asynchronous operations, makes it easy to write and understand such code. The following code starts a background thread, and then iterates the collection in parallel fashion, and after each iteration it updates the progress bar.

private void Form1_Load(object sender, EventArgs e)
{
var range = Enumerable.Range(0, 100);

Task.Factory.StartNew(() =>
{
Parallel.ForEach(range, i =>
{
progressBar.Invoke((Action) delegate { ReportProgress(i); });
Thread.Sleep(100);
});
});

}

private void ReportProgress(int value)
{
progressBar.Value = value;
}

Starting a new thread has allowed us to complete the operation in background. We have used Invoke method to let the ReportProgress method to access UI and update the progress bar. Be careful with the Parallel.For kind of methods inside UI thread. Never run them without starting a new thread. It may result in state corruption, unexpected exceptions and so on.

Exception Handling

Handling exceptions is different and more efficient than before. Exceptions thrown in the tasks, are combined into one AggregateException instance, which is propagated to the thread that joins the task. You can loop through the InnerExceptions property of it, find out exactly what happened there during the execution, and based on the exceptions you can decide next course of action.

The following obviously will not work, because the threads will not be running in this thread:

try
{
Task.Factory.StartNew(() =>
{
// Do stuffs.
});
}
catch (Exception e)
{
// Won't work
}

We can only expect the exceptions to come when the tasks join back, like in Wait or when we attempt to access task.Result:

var task = Task.Factory.StartNew(() =>
{
throw new ArgumentNullException();
});

try
{
task.Wait();
}
catch (AggregateException ae)
{
// Will work
foreach(var exception in ae.InnerExceptions)
{
if(exception is ArgumentNullException)
{

}
else if(exception is IndexOutOfRangeException)
{

}
else
{

}
}
}


Task Parallel Library is an excellent collection of classes for almost all the parallelism needs you may encounter while developing applications in .NET. You can expect the same behavior while you debug. You can set breakpoints as usual and step through. Even if you are developing for single processor, TPL keeps you one step ahead and makes your application ready for multiprocessor machines too.

application Task (computing)

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • Top 10 Best Practices for Web Application Testing
  • DevOps for Developers: Continuous Integration, GitHub Actions, and Sonar Cloud
  • How To Build a Spring Boot GraalVM Image
  • Use Golang for Data Processing With Amazon Kinesis and AWS Lambda

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

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

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: