Parallel Programming in .NET Framework 4: Getting Started

转载 2011年01月05日 00:20:00

 

With this post I want to start a series devoted to the new parallel programming features in .NET Framework 4 and introduce you the Task Parallel Library (TPL).

Update. The list of posts in this series:

I have to admit that I’m not an expert in multithreading or parallel computing. However, people often ask me about easy introductions and beginner’s samples for new features. And I have an enormous advantage over most newbies in this area – I can ask people who developed this library about what I’m doing wrong and what to do next. By the way, if you want to ask someone about what to do next with your parallel program, I'd recommend you to go to this forum -Parallel Extensions to the .NET Framework Forum.

I have a simple goal this time. I want to parallelize a long-running console application and add a responsive WPF UI. By the way, I’m not going to concentrate too much on measuring performance. I’ll try to show the most common caveats, but in most cases just seeing that the application runs faster is good enough for me.

Now, let the journey begin. Here’s my small program that I want to parallelize. The SumRootN method returns the sum of the nth root of all integers from one to 10 million, where n is a parameter. In the Main method, I call this method for roots from 2 through 19. I’m using the Stopwatch class to check how many milliseconds the program takes to run.

 

On my 3-GHz dual-core 64-bit computer with 4 GB of RAM the program takes about 18 seconds to run.

Since I’m using a for loop, the Parallel.For method is the easiest way to add parallelism. All I need to do is replace

 

with the following parallel code:

 

Notice how little the code changed. I supplied start and end indices (same as I did in the simple loop) and a delegate in the form of a lambda expression. I didn’t have to change anything else, and now my little program takes about 9 seconds.

When you use the Parallel.For method, the .NET Framework automatically manages the threads that service the loop, so you don’t need to do this yourself. But remember that running code in parallel on two processors does not guarantee that the code will run exactly twice as fast. Nothing comes for free; although you don’t need to manage threads yourself, the .NET Framework still uses them behind the scenes. And of course this leads to some overhead. In fact, if your operation is simple and fast and you run a lot of short parallel cycles, you may get much less benefit from parallelization than you might expect.

Another thing you probably noticed when you run the code is that now you don’t see the results in the proper order: Instead of seeing increasing roots, you see quite a different picture. But let’s pretend that we just need results, without any specific order. In this blog post, I’m going to leave this problem unresolved.

Now it’s time to take things one step further. I don’t want to write a console application; I want some UI. So I’m switching to Windows Presentation Foundation (WPF). I have created a small window that has only one Start button, one text block to display results, and one label to show elapsed time.

The event handler for the sequential execution looks pretty simple:

 

 

Compile and run the application to make sure that everything works fine. As you might notice, UI is frozen and the text block does not update until all of the computations are done. This is a good demonstration of why WPF recommends never executing long-running operations in the UI thread.

Let’s change the for loop to the parallel one:

 

Click the button…and…get an InvalidOperationException that says “The calling thread cannot access this object because a different thread owns it.”

What happened? Well, as I mentioned earlier, the Task Parallel Library still uses threads. When you call theParallel.For method, the .NET Framework starts new threads automatically. I didn’t have problems with the console application because the Console class is thread safe. But in WPF, UI components can be safely accessed only by a dedicated UI thread. Since Parallel.For uses worker threads besides the UI thread, it’s unsafe to manipulate the text block directly in the parallel loop body. If you use, let’s say, Windows Forms, you might have different problems, but problems nonetheless (another exception or even an application crash).

Luckily, WPF provides an API that solves this problem. Most controls have a special Dispatcher object that enables other threads to interact with the UI thread by sending asynchronous messages to it. So our parallel loop should actually look like this:

 

In the above code, I’m using the Dispatcher to send a delegate to the UI thread. The delegate will be executed when the UI thread is idle. If UI is busy doing something else, the delegate will be put into a queue. But remember that this type of interaction with the UI thread may slow down your application.

Now I have our parallel WPF application running on my computer almost twice fast. But what about this freezing UI? Don’t all modern applications have responsive UI? And if Parallel.For starts new threads, why is the UI thread still blocked?

The reason is that Parallel.For tries to exactly imitate the behavior of the normal for loop, so it blocks the further code execution until it finishes all its work.

Let’s take a short pause here. If you already have an application that works and satisfies all your requirements, and you want to simply speed it up by using parallel processing, it might be enough just to replace some of the loops withParallel.For or Parallel.ForEach. But in many cases you need more advanced tools.

To make the UI responsive, I am going to use tasks, which is a new concept introduced by the Task Parallel Library. A task represents an asynchronous operation that is often run on a separate thread. The .NET Framework optimizes load balancing and also provides a nice API for managing tasks and making asynchronous calls between them. To start an asynchronous operation, I’ll use the Task.Factory.StartNew method.

So I’ll delete the Parallel.For and replace it with the following code, once again trying to change as little as possible.

 

 

Compile, run… Well, UI is responsive. I can move and resize the window while the program calculates the results. But I have two problems now:

1. My program tells me that it took 0 milliseconds to execute.

2. The program calculates the method only for root 20 and shows me a list of identical results.

Let’s start with the last one. C# experts can shout it out: closure! Yes, i is used in a loop, so when a thread starts working,i’s value has already changed. Since i is equal to 20 when the loop is exited, this is the value that is always passed to newly created tasks.

Problems with closure like this one are common when you deal with lots of delegates in the form of lambda expressions (which is almost inevitable with asynchronous programming), so watch out for it. The solution is really easy. Just copy the value of the loop variable into a variable declared within the loop. Then use this local variable instead of the loop variable.

 

Now let’s move to the second problem: The execution time isn’t measured. I perform the tasks asynchronously, so nothing blocks the code execution. The program starts the tasks and goes to the next line, which is reading time and displaying it. And it doesn’t take that long, so I get 0 on my timer.

Sometimes it’s OK to move on without waiting for the threads to finish their jobs. But sometimes you need to get a signal that the work is done, because it affects your workflow. A timer is a good example of the second scenario.

To get my time measurement, I have to wrap code that reads the timer value into yet another method from the Task Parallel Library: TaskFactory.ContinueWhenAll. It does exactly what I need: It waits for all the threads in an array to finish and then executes the delegate. This method works on arrays only, so I need to store all the tasks somewhere to be able to wait for them all to finish.

Here’s what my final code looks like:

 

Finally, everything works as expected: I have a list of results, the UI does not freeze, and the elapsed time displays correctly. The code definitely looks quite different from what I started with, but surprisingly it’s not that long, and I was able to reuse most of it (thanks to lambda expression syntax).

Again, I tried to cover common problems that most beginners in parallel programming and multithreading are likely to encounter, without getting too deep into details. But I expect this to be just the first post in a series. But if you don’t want to wait for my next posts, check out Parallel Programming in the .NET Framework on MSDN and the parallel framework team blog.

Of course, as with any other post I publish on this blog, I hope that it’s useful for you. But this one is a little bit different from the pure C# posts that I’ve written before. So, here’s a question: How useful is this type of beginner’s content to you? Do you want to see more of it? And if you’re thinking about using parallel and asynchronous programming now, what kind of scenario do you have in mind? I really need your feedback, and I’m looking forward to your comments.

P.S.

Thanks to everybody who helped me this time: Dmitry Lomov and Michael Blome for getting me started with TPL, Danny Shih and Mads Torgersen for reviewing this and providing comments, Mick Alberts and Robin Reynolds-Haertle for editing.

(转自:http://blogs.msdn.com/b/csharpfaq/archive/2010/06/01/parallel-programming-in-net-framework-4-getting-started.aspx

 

2016.9.13 Programming for Everybody (Getting Started with Python)

传送门:https://www.coursera.org/account/accomplishments/certificate/JMA8ZGF6ZT6R
  • qq_33638791
  • qq_33638791
  • 2016年09月13日 11:45
  • 445

译文:使用MVC5的Entity Framework 6入门(十二)——为ASP.NET MVC应用程序使用高级功能

为ASP.NET MVC应用程序使用高级功能这是微软官方教程Getting Started with Entity Framework 6 Code First using MVC 5 系列的翻译,这...
  • yym373872996
  • yym373872996
  • 2017年04月08日 14:40
  • 1631

TensorFlow学习篇【1】Getting Started With TensorFlow

学习网址:https://www.tensorflow.org/get_started/get_started This guide gets you started programming...
  • lingyu666hapy
  • lingyu666hapy
  • 2017年03月14日 16:52
  • 729

译文:使用MVC5的Entity Framework 6入门(五)——MVC程序中实体框架的Code First迁移和部署

MVC程序中实体框架的Code First迁移和部署这是微软官方教程Getting Started with Entity Framework 6 Code First using MVC 5 系列的...
  • yym373872996
  • yym373872996
  • 2016年10月29日 17:49
  • 549

【附原文:深度学习-开始Tensorflow】1.Getting Started With TensorFlow

谷歌的深度学习框架Tensorflow最近发布了1.0版本,于是开始自己边学习边翻译,希望能实现从0到1的突破...
  • z_somewhere
  • z_somewhere
  • 2017年03月04日 17:09
  • 352

《Getting Started with WebRTC》第二章 WebRTC技术介绍

《Getting Started with WebRTC》第二章 WebRTC技术介绍 本章作WebRTC的技术介绍,主要讲以下的概念:   .  如何建立P2P的通信   .  有效的信令...
  • fireroll
  • fireroll
  • 2015年03月12日 21:50
  • 1695

Getting started with Shell Programming

Getting started with Shell Programming In this part of tutorial you are introduce to shell programm...
  • ccssddnnbbookkee
  • ccssddnnbbookkee
  • 2014年03月20日 19:25
  • 586

LLVM每日谈之十九 LLVM的第一本系统的书<Getting Started with LLVM Core Libraries>

作者:史宁宁(snsn1984)LLVM终于有了一本系统的书了——《Getting Started with LLVM Core Libraries》。这本书号称是LLVM的第一本书,但是据说日本早就...
  • snsn1984
  • snsn1984
  • 2014年11月05日 09:59
  • 5284

Heterogeneous Parallel Programming(异构并行编程)学习笔记(一)

好记性不如烂笔记。以下是在Coursera学习Heterogeneous Parallel Programming时记录的一些要点。 Wiki对Heterogeneous Programmi...
  • huhumama0
  • huhumama0
  • 2012年12月13日 22:29
  • 5550

Getting started Play Framework

1.What is Play? The High Velocity Web Framework For Java and Scala. Play Framework makes it easy to...
  • github_22022001
  • github_22022001
  • 2016年04月15日 17:30
  • 1142
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Parallel Programming in .NET Framework 4: Getting Started
举报原因:
原因补充:

(最多只允许输入30个字)