Sunday, October 16, 2011

Task Parallel Library (TPL)–The lambda strikes back

 

Hi All,

I would like to share an interesting insight I got when solving some problems at Project Euler.  I was trying to make my program run a little faster (because it was using only one CPU core) so I decided to use the TPL. I will spare you the details of the actual problem and instead present a simple piece of code which demonstrates the problem:

  1. public static void Run()
  2.     {
  3.       Task[] tasks = new Task[10];
  4.       for (int i = 0; i < 10; i++)
  5.       {
  6.         tasks[i] = Task.Factory.StartNew(() => { PrintNumber(i); });
  7.       }
  8.  
  9.       Task.WaitAll(tasks);
  10.     }
  11.  
  12.     private static void PrintNumber(int i)
  13.     {
  14.       Thread.Sleep(100);
  15.       Console.Write(i);
  16.       Console.Write(",");
  17.     }

What do you think will be printed when I call Run?

 

If you think that this is not deterministic because the TPL may choose to run the tasks at any order you would be half right. Indeed, the TPL can run the tasks in any order but in this case we have a different problem. The evaluation of the lambda expression will be done only when the task is run and in the general case it would be after the loop is finished so the value of “i" would be 10 for all the tasks.

The correct answer is therefore that the program will print 10 times “10,” (in the general case because in some cases the task may start in the middle of the loop).

I remind you from a previous post on my blog that when you use a local variable in a lambda expression the compiler generates a “secret” class for you which holds a reference (for reference types) or a copy (for value types) of this variable until the lambda expression is run. Therefore to get the desired result we need a unique variable to be copied in each iteration. This can be achieved by adding a temp variable within the scope of the loop and use it in the lambda.

  1. public static void Run()
  2. {
  3.   Task[] tasks = new Task[10];
  4.   for (int i = 0; i < 10; i++)
  5.   {
  6.     int x = i;
  7.     tasks[i] = Task.Factory.StartNew(() => { PrintNumber(x); });
  8.   }
  9.  
  10.   Task.WaitAll(tasks);
  11. }
  12.  
  13. private static void PrintNumber(int i)
  14. {
  15.   Thread.Sleep(100);
  16.   Console.Write(i);
  17.   Console.Write(",");
  18. }

Now we get the correct results, the numbers 0 though 9 are printed (in some order).

My conclusion is (again): One should be extra careful when using local variables in lambda expressions.

 

Thanks for reading,

Boris.

Thursday, October 13, 2011

Persisting a GridSplitter

Hi All,

If you are using WPF then you probably use the Grid container and if you are using that then at times you surely need the assistance of the GridSplitter. The GridSplitter is a sub element of the Grid and it allows the user to conveniently resize the grid columns or rows. Those of you who worked with the GridSplitter know that it is quite challenging to work with correctly.

I will start with a small example. In my application I need a vertical splitter between two buttons (conveniently named Left and Right). The initial size of the Left button should be twice the size of the Right button. Here is the XAML:

  1. <Grid>
  2.     <Grid.ColumnDefinitions>
  3.         <ColumnDefinition Width="2*"></ColumnDefinition>
  4.         <ColumnDefinition Width="Auto"></ColumnDefinition>
  5.         <ColumnDefinition Width="*"></ColumnDefinition>
  6.     </Grid.ColumnDefinitions>
  7.     <GridSplitter Width="5" Background="Red" VerticalAlignment="Stretch" HorizontalAlignment="Center" Grid.Column="1"></GridSplitter>
  8.     <Button Grid.Column="0">Left</Button>
  9.     <Button Grid.Column="2">Right</Button>
  10. </Grid>

And this is the result:

p2

I am using a technique where the GridSplitter sits in its own column. I find this approach better since then you don’t need to think how it should be position within the column or within the XAML file.

The problem with the GridSplitter becomes apparent when you try to use fixed sizes in the grid column width (the same applies for grid row height but I will continue the discussion with the grid columns). What if we want the left column to start with a fixed size of 5cm on the screen. The logical thing to do would be to set the width of the first column to 5cm. If you do that and run the application everything would seem normal until you start dragging the GridSplitter. Try dragging it to the right… way right… past the window border right… now let go and try to drag it back. That’s right. The GridSplitter is gone! I read somewhere on the Internet that the grid splitter does exactly what I told it to do. If you believe that then try to do the same with the right column and dragging the GridSplitter left. The phenomenon here is even stranger.

  1. <Grid>
  2.      <Grid>
  3.          <Grid.ColumnDefinitions>
  4.              <ColumnDefinition Width="*" ></ColumnDefinition>
  5.              <ColumnDefinition Width="Auto"></ColumnDefinition>
  6.              <ColumnDefinition Width="5cm" ></ColumnDefinition>
  7.          </Grid.ColumnDefinitions>
  8.  
  9.          <GridSplitter Width="5" Background="Red" VerticalAlignment="Stretch"
  10.     HorizontalAlignment="Center" Grid.Column="1"></GridSplitter>
  11.          <Button Grid.Column="0">Left</Button>
  12.          <Button Grid.Column="2" >Right</Button>
  13.      </Grid>
  14.  </Grid>

Don’t do that… Its crazy!

The point I am trying to make here is that the GridSplitter is best used when the widths of the column are star based. You can have some control using the MinWidth and MaxWidth properties applied on the columns, but sometimes this behavior is not the desired one.

Now for the main challenge. I want my application to restore the state of the GridSplitter position next time it is run. For example if I move the GridSplitter such that only the Right button is visible and close the application. When I start the application I expect only the Right button to be visible. Actually doing this is quite simple. I add x:Name attributes to the grid columns and handle the DragCompleted event of the GridSplitter. In the handler I serialize the width properties of both columns into a file. The problem here is that the Width is not a simple double value but a struct (GridLength). Fortunately, the framework provides us with a small helper class to serialize and deserialize this struct called GridLengthConverter. The serialization code looks like this:

  1. Assembly assembly = Assembly.GetExecutingAssembly();
  2. String directory = System.IO.Path.GetDirectoryName(assembly.Location);
  3. using (StreamWriter writer = new StreamWriter(Path.Combine(directory, "width.txt")))
  4. {
  5.   GridLengthConverter converter = new GridLengthConverter();
  6.   writer.WriteLine(converter.ConvertToString(LeftColumn.Width));
  7.   writer.WriteLine(converter.ConvertToString(RightColumn.Width));
  8. }

and the deserialization code is therefore like this:

  1. Assembly assembly = Assembly.GetExecutingAssembly();
  2. String directory = System.IO.Path.GetDirectoryName(assembly.Location);
  3. if (File.Exists(Path.Combine(directory, "width.txt")))
  4. {
  5.   using (StreamReader reader = new StreamReader(Path.Combine(directory, "width.txt")))
  6.   {
  7.     GridLengthConverter converter = new GridLengthConverter();
  8.     LeftColumn.Width = (GridLength)converter.ConvertFromString(reader.ReadLine());
  9.     RightColumn.Width = (GridLength)converter.ConvertFromString(reader.ReadLine());
  10.   }
  11. }

(it is run after InitializeComponents).

The important thing to notice here is that you must serialize both column width because they are relative to each other.

That’s it for today. Thank you for reading.

Boris.