Wednesday, June 17, 2009

C#: Simple Threading Example Using ThreadPool

With machines now being sold with multiple cores, why should your application be using only one? Harness the full potential of the processor(s) and make your app shine!

Threading seems so complicated, it really isn’t. Today, I’ll provide an example of the simplest thread code you’ve ever seen. In fact, it’s so simple, here it is:

class TheTest
{
public void Go()
{
for (int i = 0; i < 100; i++)
{
WaitCallback wcb = new WaitCallback(this.DoWork);
ThreadPool.QueueUserWorkItem(wcb, i);
}
Console.ReadLine();
}

public void DoWork(object state)
{
int i = (int)state;
Console.WriteLine(string.Format("{0:d3} started",i));
Thread.Sleep(500);
Console.WriteLine(string.Format("{0:d3} done", i));
}
}

Create a new instance of the TheTest class, call go. You’ll see 100 threads get created and do work. You can pass in anything you want for the state. That’s a very good place to put your “unit of work” that the thread must work on. Just make sure you add a using clause for System.Threading.

Of course, this post doesn’t touch on any locking or blocking issues, I’ll do that later. But for now, why not multithread?

Linq Performance

I needed to clear all the textboxes in a windows form without specifying each and every textbox. Easy enough to do in C#:



foreach (Control c in Controls)
{
if (c is TextBox)
((TextBox)c).Clear();

if (c is ListBox)
((ListBox)c).Items.Clear();

if (c is Panel)
{
foreach (Control c2 in c.Controls)
{
if (c2 is TextBox)
{
((TextBox)c2).Clear();
}
}
}
}

To me, that just seemed expensive. I haven't had a chance to play with Linq, until now. So I came up with the Linq equivilant:



foreach (TextBox tb in from Control control in Controls
where control is TextBox
select (TextBox)control)
{
tb.Clear();
}

foreach (ListBox lb in from Control control in Controls
where control is ListBox
select (ListBox)control)
{
lb.Items.Clear();
}

foreach (TextBox tb in from Control control in panel1.Controls
where control is TextBox
select (TextBox)control)
{
tb.Clear();
}

For me, this is actually easier to read! OK, but performance must be horrible, but I'm not one to judge without an actual test. I used the System.Diagnostics.Stopwatch class like this:



System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch();
stopwatch.Start();
// Do stuffstopwatch.Stop();
MessageBox.Show(string.Format("linq: {0:#,0}", stopwatch.ElapsedTicks));


I had some very interesting numbers! For a form with about 25 controls, I got these numbers:
Linq averaged 3,700,000 ticks on first run
Foreach averaged 4,400,000 ticks on first run


Clicking the Clear button a second time resulted in faster times for both Linq and Foreach, probably due to a form of caching:
Linq averaged 333,000 ticks on subsequent runs
Foreach averaged 1,010,000 ticks on subsequent runs

So, Linq must be optimized for ControlCollection, which is derived from ArrangedElementCollection, which implements IList, ICollection, IEnumerable, ICloneable. I'll have to do more investigation to see where foreach is faster than Linq and post that result later.

Tuesday, June 16, 2009

C# String.Format("format", arguments)

This is my personal reference for String.Format().

Here are the quick reference tables.  Below are the explanations.


Format

Int 1234

Decimal

1234.567

Decimal

0.1234567

Decimal
1234.5

0:c

$1,234.00

$1,234.57

$0.12

$1,234.50

0:d

1234

-

-

-

0:e

1.234000e
+003


1.234567e+003

1.234567e-001

1.234500e+003

0:f

1234.00

1234.57

0.12

1234.50

0:g

1234

1234.567

0.1234567

1234.5

0:n

1234.00

1,234.57

0.12

1,234.50

0:p

123,400.00
%


123,456.70
%


12.35 %

123,450.00
%


0:r

-

-

-

-

0:x

4d2

-

-

-

0:X

4D2

-

-

-

Padding/Precision:







0:d5

01234

-

-

-

0:e5

1.23400e+003

1.23457e+003

1.23457e-001

1.23450e+003

0:n5

1,234.00000

1,234.56700

0.12346

1,234.50000

0:c5

$1,234.00000

$1,234.56700

$0.12346

$1,234.50000

0:x5

004d2

-

-

-


Using: DateTime.Parse("December
13, 2008 5:01:30.7770 pm"
);



Format

DateTime (above date)

Jan
5 (rest same)


Description

0:D

Saturday, December
13, 2008


Wednesday, January
09, 2008


Long Date

0:F

Saturday, December
13, 2008 5:01:30 PM


Wednesday, January
09, 2008 5:01:30 PM


Full Date/Time
(long time)


0:G

12/13/2008 5:01:30
PM


1/9/2008 5:01:30 PM

General Date/Time
(long time)


0:M

December 13

January 09

Month Day

0:O

2008-12-13T17:01:30.7770000

2008-01-09T17:01:30.7770000

Round-Trip
Date/Time


0:R

Sat, 13 Dec 2008
17:01:30 GMT


Wed, 09 Jan 2008
17:01:30 GMT


RFC1123

0:T

5:01:30
PM


5:01:30
PM


Long
Time


0:U

Saturday, December
13, 2008 10:01:30 PM


Wednesday, January
09, 2008 10:01:30 PM


Universal Full
Date/Time (long time)


0:Y

December, 2008

January, 2008

Year Month

0:d

12/13/2008

1/9/2008

Short Date

0:f

Saturday,
December 13, 2008 5:01 PM


Wednesday,
January 09, 2008 5:01 PM


Full Date/Time
(short time)


0:g

12/13/2008 5:01 PM

1/9/2008 5:01 PM

General Date/Time (short time)

0:m

December
13


January
09


Month
Day


0:o

2008-12-13T17:01:30.7770000

2008-01-09T17:01:30.7770000

Round-Trip Date/Time

0:r

Sat, 13
Dec 2008 17:01:30 GMT


Wed, 09
Jan 2008 17:01:30 GMT


RFC1123

0:s

2008-12-13T17:01:30

2008-01-09T17:01:30

Sortable Date/Time

0:t

5:01 PM

5:01 PM

Short
Time


0:u

2008-12-13 17:01:30Z

2008-01-09 17:01:30Z

Universal Sortable
Date/Time


0:y

December,
2008


January,
2008


Year
Month




Useful Examples


Format

Use

Output

0:000-00-0000

SSN

012-34-5678

0:(###)###-####

US Phone Number

(555)555-5555

0:#-(###)###-####

US Phone
Number


1-(555)555-5555

0:1-(###)###-####

US Phone Number

1-(555)555-5555



Formatting DateTime objects with DateTime.ToString("Format Here"):


Format

Description

d

Non-padded
day


dd

Zero padded day

ddd

Short day
name (Tue)


dddd

Full day name (Tuesday)

h

Non-padded hour 12
hour format


hh

Zero padded hour 12
hour format


H

Non-padded hour 24
hour format


HH

Zero padded hour 24
hour format


m

Non-padded minute

mm

Zero padded minute

M

Non-padded Month

MM

Zero padded Month

MMM

Short month name
(Jun)


MMMM

Full month name
(June)


s

Non-padded seconds

ss

Zero-padded seconds

tt

AM/PM

yy

2 digit year

yyyy

4 digit year

:

As is

/

As is

MM/DD/yyyy

Sample: 06/14/2008

You can customize the above with Number Customizations:

# is a digit place holder.

0 is a zero place holder.

. is a printed decimal point.

, is a printed comma.

% means use the number as a percentage where 1 is 100%.

If you want to have the actual number be the percentage, prefix the % with a '.
Example:

String.Format("{0:##.00'%",
12.34); // 12.34%


To do left and right padding:

String.Format("{0,-9}",
1234); // “1234


String.Format("{0,9}", 1234); // “     1234“


The Basics

How to call String.Format(): String.Format("{ArgumentPosition,Padding}",
arguments[] or argument,argument…);
 

String.Format("{0} {1}",
"Hello", "World");

Prints: Hello World

{0} and {1} are the positions of the arguments “Hello” and “World” specifying
where in the string they are top be used.

String.Format("First
Number: {0}, Third Number: {2}, Second Number: {1}."
, 1,2,3);

Prints:
First Number: 1, Third Number: 3, Second Number: 2.


You do not have to call ToString() on your arguments, String.Format()
is smart enough to do that for us. Suppose you created your own Class called
Currency, which overrides ToString(). When your instance of Currency has a value of $19.95,
you can use:

String.Format("MyCurrency value:
{0}"
, myCurrency);


Prints: MyCurrency value: $19.95

Continue that thought just a little further... if you print DataRow information:

String.Format("Column
'ID': {0}"
, dr["ID"]);