Microsoft Visual C# 2010 Step by Step (P15) pptx

50 350 0
Microsoft Visual C# 2010 Step by Step (P15) pptx

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

Thông tin tài liệu

670 Part VI Building Professional Solutions with Visual Studio 2010 The following code shows how to use a BlockingCollection<T> object to wrap an instance of a user-defined type called MyCollection<T> that implements the IProducerConsumerCollection interface. It limits the number of items in the underlying collection to 1000. class MyCollection<T> : IProducerConsumerCollection<T> { // Implementation details not shown } // Create an instance of MyCollection<T>, // and wrap it in a BlockingCollection<T> object MyCollection<int> intCollection = new MyCollection<int>(); BlockingCollection<int> collection = new BlockingCollection<int>(myCollection, 1000); Note You can also instantiate a BlockingCollection<T> object without specifying a collec- tion class. In this case, the BlockingCollection<T> object creates a ConcurrentQueue<T> object internally. Adding thread-safety to the methods in a collection class imposes additional runtime over- head, so these classes are not as fast as the regular collection classes. You need to bear this fact in mind when deciding whether to parallelize a set of operations that require access to a shared collection. Using a Concurrent Collection and a Lock to Implement Thread-Safe Data Access In the following set of exercises, you will implement an application that calculates PI by using a geometric approximation. Initially, you will perform the calculation in a single-threaded manner; then you will change the code to perform the calculation by using parallel tasks. In the process, you will uncover some data synchronization issues you need to address and that you will solve by using a concurrent collection class and a lock to ensure that the tasks coordinate their activities correctly. The algorithm you will implement calculates PI based on some simple mathematics and statistical sampling. If you draw a circle of radius r and draw a square with sides that touch the circle, the sides of the square are 2 * r in length as shown in the following image: Chapter 28 Performing Parallel Data Access 671 The area of the square, S, is calculated as follows: (2 * r) * (2 * r) or 4 * r * r The area of the circle, C, is calculated as follows: PI * r * r Using these areas, you can calculate PI as follows: 4 * C / S The trick is to determine the value of the ratio C / S. This is where the statistical sampling comes in. To do this, generate a set of random points that lie within the square and count how many of these points also fall within the circle. If you have generated a sufficiently large and random sample, the ratio of points that lie within the circle to the points that lie within the square (and also in the circle) approximates the ratio of the areas of the two shapes, C / S. All you have to do is count them. How do you determine whether a point lies within the circle? To help visualize the solution, draw the square on a piece of graph paper with the center of the square at the origin, point (0, 0). You can then generates pairs of values, or coordinates, that lie within the range (-r, -r) to (+r, +r). You can determine whether any set of coordinates (x, y) lie within the circle by ap- plying Pythagoras’ theorem to determine the distance d of these coordinates from the origin. 672 Part VI Building Professional Solutions with Visual Studio 2010 You can calculate d as the square root of ((x * x) + (y * y)). If d is less than or equal to r, the radius of the circle, then the coordinates (x, y) specify a point within the circle, as shown in the following diagram: You can simplify matters further by generating only coordinates that lie in the upper right quadrant of the graph so that you only have to generate pairs of random numbers between 0 and r. This is the approach you will take in the exercises. Note The exercises in this chapter are intended to run on a computer with a multicore proces- sor. If you have only a single-core CPU, you will not observe the same effects. Also, you should not start any additional programs or services between exercises because these might affect the results you see. Calculate PI by using a single thread 1. Start Microsoft Visual Studio 2010 if it is not already running. 2. Open the CalculatePI solution, located in the \Microsoft Press\Visual CSharp Step By Step\Chapter 28\CalculatePI folder in your Documents folder. 3. In Solution Explorer, double-click Program.cs to display the file in the Code and Text Editor window. This is a console application. The skeleton structure of the application has already been created for you. Chapter 28 Performing Parallel Data Access 673 4. Scroll to the bottom of the file, and examine the Main method. It looks like this: static void Main(string[] args) { double pi = SerialPI(); Console.WriteLine("Geometric approximation of PI calculated serially: {0}", pi); Console.WriteLine(); pi = ParallelPI(); Console.WriteLine("Geometric approximation of PI calculated in parallel: {0}", pi); } This code calls the SerialPI method, which will calculate PI by using the geometric al- gorithm described before this exercise. The value is returned as a double and displayed. The code then calls the ParallelPI method, which will perform the same calculation but by using concurrent tasks. The result displayed should be exactly the same as that re- turned by the SerialPI method. 5. Examine the SerialPI method. static double SerialPI() { List<double> pointsList = new List<double>(); Random random = new Random(SEED); int numPointsInCircle = 0; Stopwatch timer = new Stopwatch(); timer.Start(); try { // TO DO: Implement the geometric approximation of PI return 0; } finally { long milliseconds = timer.ElapsedMilliseconds; Console.WriteLine("SerialPI complete: Duration: {0} ms", milliseconds); Console.WriteLine("Points in pointsList: {0}. Points within circle: {1}", pointsList.Count, numPointsInCircle); } } This method will generate a large set of coordinates and calculates the distances of each set of coordinates from the origin. The size of the set is specified by the constant NUMPOINTS at the top of the Program class. The bigger this value is, the greater the set of coordinates and the more accurate is the value of PI calculated by this method. If you have sufficient memory, you can increase the value of NUMPOINTS. Similarly, if you find that the application throws OutOfMemoryException exceptions when you run it, you can reduce this value. 674 Part VI Building Professional Solutions with Visual Studio 2010 You store the distance of each point from the origin in the pointsList List<double> collection. The data for the coordinates is generated by using the random variable. This is a Random object, seeded with a constant to generate the same set of random num- bers each time you run the program. (This helps you determine that it is running cor- rectly.) You can change the SEED constant at the top of the Program class if you want to seed the random number generator with a different value. You use the numPointsInCircle variable to count the number of points in the pointsList collection that lie within the bounds of the circle. The radius of the circle is specified by the RADIUS constant at the top of the Program class. To help you compare performance between this method and the ParallelPI method, the code creates a Stopwatch variable called timer and starts it running. The finally block determines how long the calculation took and displays the result. For reasons that will be described later, the finally block also displays the number of items in the pointsList collection and the number of points that it found that lay within the circle. You will add the code that actually performs the calculation to the try block in the next few steps. 6. In the try block, delete the comment and remove the return statement. (This statement was provided only to ensure that the code compiles.) Add the for block and statements shown next in bold to the try block: try { for (int points = 0; points < NUMPOINTS; points++) { int xCoord = random.Next(RADIUS); int yCoord = random.Next(RADIUS); double distanceFromOrigin = Math.Sqrt(xCoord * xCoord + yCoord * yCoord); pointsList.Add(distanceFromOrigin); doAdditionalProcessing(); } } This block of code generates a pair of coordinate values that lie in the range 0 to RADIUS, and it stores them in the xCoord and yCoord variables. The code then uses Pythagoras’ theorem to calculate the distance of these coordinates from the origin and adds the result to the pointsList collection. Note Although there is a little bit of computational work performed by this block of code, in a real-world scientific application you are likely to include far more complex cal- culations that will keep the processor occupied for longer. To simulate this situation, this block of code calls another method, doAdditionalProcessing. All this method does is oc- cupy a number of CPU cycles as shown in the following code sample. I opted to follow this approach to better demonstrate the data synchronization requirements of multiple tasks rather than have you write an application that performs a highly complex calculation such as a Fast Fourier Transform to keep the CPU occupied: Chapter 28 Performing Parallel Data Access 675 private static void doAdditionalProcessing() { Thread.SpinWait(SPINWAITS); } SPINWAITS is another constant defined at the top of the Program class. 7. In the SerialPI method, in the try block, add the foreach statement shown next in bold after the for block: try { for (int points = 0; points < NUMPOINTS; points++) { } foreach (double datum in pointsList) { if (datum <= RADIUS) { numPointsInCircle++; } } } This code iterates through the pointsList collection and examines each value in turn. If the value is less than or equal to the radius of the circle, it increments the numPointsIn- Circle variable. At the end of this loop, numPointsInCircle should contain the total num- ber of coordinates that were found to lie within the bounds of the circle. 8. Add the following statements shown in bold to the try block, after the foreach block: try { for (int points = 0; points < NUMPOINTS; points++) { } foreach (double datum in pointsList) { } double pi = 4.0 * numPointsInCircle / NUMPOINTS; return pi; } These statements calculate PI based on the ratio of the number of points that lie within the circle to the total number of points, using the formula described earlier. The value is returned as the result of the method. 9. On the Debug menu, click Start Without Debugging. 676 Part VI Building Professional Solutions with Visual Studio 2010 The program runs and displays its approximation of PI, as shown in the following image. (It took just over 46 seconds on my computer, so be prepared to wait for a little while.) The time taken to calculate the result is also displayed. (You can ignore the results from the ParallelPI method because you have not written the code for this method yet.) Note Apart from the timing, your results should be the same unless you have changed the NUMPOINTS, RADIUS, or SEED constants. 10. Close the console window, and return to Visual Studio. In the SerialPI method, the code in the for loop that generates the points and calculates their distance from the origin is an obvious area that can parallelized. This is what you will do in the next exercise. Calculate PI by using parallel tasks 1. In Solution Explorer, double-click Program.cs to display the file in the Code and Text Editor window if it is not already open. 2. Locate the ParallelPI method. It contains exactly the same code as the initial version of the SerialPI method before you added the code to the try block to calculate PI. 3. In the try block, delete the comment and remove the return statement. Add the Parallel.For statement shown next in bold to the try block: try { Parallel.For (0, NUMPOINTS, (x) => { int xCoord = random.Next(RADIUS); int yCoord = random.Next(RADIUS); double distanceFromOrigin = Math.Sqrt(xCoord * xCoord + yCoord * yCoord); Chapter 28 Performing Parallel Data Access 677 pointsList.Add(distanceFromOrigin); doAdditionalProcessing(); }); } This construct is the parallel analog of the code in the for loop in the SerialPI method. The body of the original for loop is wrapped in a lambda expression. 4. Add the following code shown in bold to the try block after the Parallel.For statement. This code is exactly the same as the corresponding statements in the SerialPI method. try { Parallel.For ( ); foreach (double datum in pointsList) { if (datum <= RADIUS) { numPointsInCircle++; } } double pi = 4.0 * numPointsInCircle / NUMPOINTS; return pi; } 5. On the Debug menu, click Start Without Debugging. The program runs. The following image shows the typical output: The value calculated by the SerialPI method should be exactly the same as before. However, the result of the ParallelPI method looks somewhat suspect. The random number generator is seeded with the same value as that used by the SerialPI method, so it should produce the same sequence of random numbers with the same result and 678 Part VI Building Professional Solutions with Visual Studio 2010 the same number of points within the circle. Another curious point is that the point- sList collection in the ParallelPI method seems to contain fewer points than the same collection in the SerialPI method. Note If the pointsList collection actually contains the expected number of items, run the application again. You should find that it contains fewer items than expected in most (but not necessarily all) runs. 6. Close the console window, and return to Visual Studio. So what went wrong with the parallel calculation? A good place to start is the number of items in the pointsList collection. This collection is a generic List<double> object. However, this type is not thread-safe. The code in the Parallel.For statement calls the Add method to append a value to the collection, but remember that this code is being executed by tasks running as concurrent threads. Consequently, given the number of items being added to the collection, it is highly probable that some of the calls to Add will interfere with each other and cause some corruption. A solution is to use one of the collections from the System. Collections.Concurrent namespace because these collections are thread-safe. The generic ConcurrentBag<T> class in this namespace is probably the most suitable collection to use for this example. Use a thread-safe collection 1. In Solution Explorer, double-click Program.cs to display the file in the Code and Text Editor window if it is not already open. 2. Locate the ParallelPI method. At the start of this method, replace the statement that in- stantiates the List<double> collection with code that creates a ConcurrentBag<double> collection, as shown here in bold: static double ParallelPI() { ConcurrentBag<double> pointsList = new ConcurrentBag <double>(); Random random = ; } 3. On the Debug menu, click Start Without Debugging. The program runs and displays its approximation of PI by using the SerialPI and ParallelPI methods. The following image shows the typical output. Chapter 28 Performing Parallel Data Access 679 This time, the pointsList collection in the ParallelPI method contains the correct number of points, but the number of points within the circle still appears to be very high; it should be the same as that reported by the SerialPI method. You should also note that the time taken by the ParallelPI method has increased significantly. This is because the methods in the ConcurrentBag<T> class have to lock and unlock data to guarantee thread safety, and this process adds to the overhead of calling these methods. You need to bear this in mind when considering whether it is appropriate to parallelize an operation. 4. Close the console window, and return to Visual Studio. You now have the correct number of points in the pointsList collection, but the values of these points is now suspect. The code in the Parallel.For construct calls the Next method of a Random object, but like the methods in the generic List<T> class this method is not thread- safe. Sadly, there is no concurrent version of the Random class, so you must resort to using an alternative technique to serialize calls to the Next method. Because each invocation is relatively brief, it makes sense to use a lock to guard calls to this method. Use a lock to serialize method calls 1. In Solution Explorer, double-click Program.cs to display the file in the Code and Text Editor window if it is not already open. 2. Locate the ParallelPI method. Modify the code in the lambda expression in the Parallel. For statement to protect the calls to random.Next by using a lock statement. Specify the pointsList collection as the subject of the lock, as shown here in bold: static double ParallelPI() { Parallel.For(0, NUMPOINTS, (x) => { int xCoord; int yCoord; [...]... environment by using IIS, this problem does not arise because IIS runs independently of Visual Studio 2010 Important  If you have been using Visual Web Developer 2010 Express for the exercises in this part of the book, start Visual C# 2010 Express rather than a second instance of Visual Web Developer 2010 Express (Leave Visual Web Developer 2010 Express running.) 2 In the second instance of Microsoft Visual. .. portable data format that is based on XML HTTP and XML are both standardized technologies that can be used by other programming environments outside the Microsoft NET Framework With Microsoft Visual Studio 2010, you can build Web services by using Microsoft Visual C++, Microsoft Visual C#, or Microsoft Visual Basic However, as far as a client application is concerned, the language used to create the Web... generated by Visual Studio 2010 whenever you create a new WCF service p ­ roject In subsequent exercises, you will define and implement the HowMuchWillItCost Web method and then test the Web method to ensure that it works as expected Important  You cannot build Web services by using Microsoft Visual C# 2010 Express Instead, you should use Microsoft Visual Web Developer 2010 Express You can download Visual. .. box, type ProductDetailsContracts 1.5 In the Location text box, specify the \Microsoft Press \Visual CSharp Step By Step\ Chapter 29 folder under your Documents folder 1.6 Click OK 2 If you are using Microsoft Visual C# 2010 Express, perform the following tasks to create a new class library project: 2.1 Start Visual C# 2010 Express if it is not already running 2.2 On the File menu, click New Project... Web Developer 2010 Express free of charge from the Microsoft Web site Create the SOAP Web service, and examine the sample code 1 Start Visual Studio 2010 if it is not already running, or start Visual Web Developer 2010 Express 2 If you are using Visual Studio 2010 Professional or Enterprise, on the File menu, point to New, and then click Web Site 3 If you are using Visual Web Developer 2010 Express,... that you select Visual C# under Installed Templates in the left pane 4 In the New Web Site dialog box, click the WCF Service template Select File System in the Location drop-down list box, specify the \Microsoft Press \Visual CSharp Step By Step\ Chapter 29\ProductInformationService folder under your Documents folder, and then click OK Visual Studio 2010 generates a Web site hosted by using the Development... that uses the methods exposed by a Web service Note  The purpose of this chapter is to provide a basic introduction to Web services and Microsoft Windows Communication Foundation (WCF) If you want detailed information about how WCF works and how to build secure services by using WCF, you should consult a book such as Microsoft Windows Communication Foundation Step by Step (Microsoft Press, 2007) 683... REST Web service 1 If you are using Visual Studio 2010 Standard or Visual Studio 2010 Professional, perform the following tasks to create a new class library project: 1.1 In the instance of Visual Studio that you used to edit the client application, on the File menu, point to New, and then click Project 1.2 In the New Project dialog box, in the left pane, under Visual C#, click Windows 1.3 In the middle... VI  Building Professional Solutions with Visual Studio 2010 Note  The solution file for a Web site project is located under the Visual Studio 2010\ Projects folder in your Documents folder rather than in the folder that contains the files for the Web site You can open an existing Web Site project either by finding and opening the appropriate solution file, or by using the Open Web Site command on the... a second instance of Visual Web Developer 2010 Express (Leave Visual Web Developer 2010 Express running.) 2 In the second instance of Microsoft Visual Studio 2010, open the ProductClient solution in the \Microsoft Press \Visual CSharp Step By Step\ Chapter 29\ProductClient folder in your Documents folder 3 In Solution Explorer, double-click the file ProductClient.xaml to display the form in the Design . used by other programming environments outside the Microsoft .NET Framework. With Microsoft Visual Studio 2010, you can build Web services by using Microsoft Visual C++, Microsoft Visual C#, . services by using Microsoft Visual C# 2010 Express. Instead, you should use Microsoft Visual Web Developer 2010 Express. You can download Visual Web Developer 2010 Express free of charge from the Microsoft. PI by using a single thread 1. Start Microsoft Visual Studio 2010 if it is not already running. 2. Open the CalculatePI solution, located in the Microsoft Press Visual CSharp Step By Step Chapter

Ngày đăng: 05/07/2014, 16:20

Từ khóa liên quan

Tài liệu cùng người dùng

Tài liệu liên quan