Saturday, September 27, 2008

Try to use "Insert Code for Windows Live Writer" for Live Writer

If you use Live Writer to post articles onto blogspot, you now can use "Insert Code for Windows Live Writer" plug-in for Live Writer. It's more convenient comparing to web page code formatter.

    public partial class Window1 : Window
{
public class InnerObject
{
public InnerObject()
{
Debug.WriteLine("InnerObject() ctor.");
}
}

public class Foo
{
private int field = 99;
private InnerObject innerObject = new InnerObject();

public Foo()
{
}

public Foo(int a)
{
}
}

public Window1()
{
InitializeComponent();

Foo foo1 = new Foo();
Foo foo2 = new Foo(12);
}
}

Try to use Live Writer

Live writer is a cool tool for you to writer blog posts. I've never used it before. A friend of mine told me that Live Writer can do that. I was so exciting because I was searching for such kind of software for a long time.

Cool indeed!

Monday, September 1, 2008

Don't forget to call EndXxx()

Don't forget to call EndXxx() if initialized an asynchronous call via BeginXxx(), otherwise there will be resource leaks. I did forget to do this in post: [Multi-Threading] Syncing between Threads by SynchronizationContext .

EndXxx() usually should be called before you want to get the result of the asynchronous operation. You can call it right after BeginXxx(), or when you get noticed about the completion of the asynchronous operation. But remember, you should call it only for one time. If you call it the second time, the behaviour are not predicatible since resources have been released during the first call to EndXxx().

Sunday, August 31, 2008

How to Avoid Duplicated Initialization of Fields


It is said in Jeff's book that initialization work of a field together with its definition will be done in ctors. If you have several ctors, the field will be initialized in these ctors seperately. See following example:
    public partial class Window1 : Window
{
public class InnerObject
{
public InnerObject()
{
Debug.WriteLine("InnerObject() ctor.");
}
}

public class Foo
{
private int field = 99;
private InnerObject innerObject = new InnerObject();

public Foo()
{
}

public Foo(int a)
{
}
}

public Window1()
{
InitializeComponent();

Foo foo1 = new Foo();
Foo foo2 = new Foo(12);
}
}

We call these two ctors. And let's have a look at IL:
.method public hidebysig specialname rtspecialname
instance void .ctor(int32 a) cil managed
{
// Code size 29 (0x1d)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldc.i4.s 99
IL_0003: stfld int32 FieldInitialization.Window1/Foo::'field'
IL_0008: ldarg.0
IL_0009: newobj instance void FieldInitialization.Window1/InnerObject::.ctor()
IL_000e: stfld class FieldInitialization.Window1/InnerObject FieldInitialization.Window1/Foo::innerObject
IL_0013: ldarg.0
IL_0014: call instance void [mscorlib]System.Object::.ctor()
IL_0019: nop
IL_001a: nop
IL_001b: nop
IL_001c: ret
} // end of method Foo::.ctor

.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// Code size 29 (0x1d)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldc.i4.s 99
IL_0003: stfld int32 FieldInitialization.Window1/Foo::'field'
IL_0008: ldarg.0
IL_0009: newobj instance void FieldInitialization.Window1/InnerObject::.ctor()
IL_000e: stfld class FieldInitialization.Window1/InnerObject FieldInitialization.Window1/Foo::innerObject
IL_0013: ldarg.0
IL_0014: call instance void [mscorlib]System.Object::.ctor()
IL_0019: nop
IL_001a: nop
IL_001b: nop
IL_001c: ret
} // end of method Foo::.ctor


Very clear. Two fields, Foo.field and Foo.innerObject have initialization code both in Foo() and Foo(int a). So if you have large number of fields, many ctors, and you care about code size, don't initialize fields with their definition. Put initialization code into one ctor (usually the one don't have parameters, default ctor), and let other cotrs call it. And more, I found another easy way to do this. Just initialize fields with their definition and let other ctors call default ctor, like this:
        public class Foo
{
private int field = 99;
private InnerObject innerObject = new InnerObject();

public Foo()
{
}

public Foo(int a) : this()
{
}
}
And let look at the IL:
.method public hidebysig specialname rtspecialname
instance void .ctor(int32 a) cil managed
{
// Code size 10 (0xa)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void FieldInitialization.Window1/Foo::.ctor()
IL_0006: nop
IL_0007: nop
IL_0008: nop
IL_0009: ret
} // end of method Foo::.ctor

.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// Code size 29 (0x1d)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldc.i4.s 99
IL_0003: stfld int32 FieldInitialization.Window1/Foo::'field'
IL_0008: ldarg.0
IL_0009: newobj instance void FieldInitialization.Window1/InnerObject::.ctor()
IL_000e: stfld class FieldInitialization.Window1/InnerObject FieldInitialization.Window1/Foo::innerObject
IL_0013: ldarg.0
IL_0014: call instance void [mscorlib]System.Object::.ctor()
IL_0019: nop
IL_001a: nop
IL_001b: nop
IL_001c: ret
} // end of method Foo::.ctor


As you can see initialization code in Foo(int a) is gone. C# compiler did the optimization. I think, it's the better way of initializing fields, though Jeff suggest to do initialization expilicitly in default ctor.

But, I have a question, can we rely on this optimization behavor of C# compiler?

Tuesday, August 26, 2008

Try to post source code in an elegant manner

Elegance source code via http://formatmysourcecode.blogspot.com/ :

private void HeavyBackgroundWork()
{
Debug.WriteLine("Background thread: " + Thread.CurrentThread.ManagedThreadId);

// To simulate a time-cost work.
Thread.Sleep(1000);

synchronizationContext.Post(new SendOrPostCallback(delegate(object param)
{
// We have to access TextInfo by synchronizationContext, otherwise
// you will meet an exception.
TextInfo.Text = "HeavyBackgroundWork() has been finished.";
}),
null);

Debug.WriteLine("HeavyBackgroundWork finished.");
}


Another code formatter: http://www.manoli.net/csharpformat/.
But it seems that it's not friendly with blogspot.

[Multi-Threading] Syncing between threads by SynchronizationContext

-----------------------------------------
Download source code package for this article:

If you can't see/download the zip file, please try to use TOR. TOR is a great tool that can break the GFW and let you access any website that prohibited by GFW.
http://vidalia-project.net/download.php
-----------------------------------------

I'd like to offer a series of tech articles on Multi-Threading which is a hot topic these days. Actually, I have a tech talk weeks later. So I'm trying to introduce my learning on this topic to our group first :) Please don't hesitate to give me suggestions to improve my knowledge.


Today's topic is syncing between threads by SynchronizationContext.

As you know, from MFC, WinForm to WPF, UI element can't be accessed from a thread other than UI thread. If you try to do this, you will meet an assertion, or exception. For WPF applications, there will be two threads. One is rendering thread, the other is UI thread. Rendering thread is responsible for rendering UI elements to graphics card, while UI thread is for managing UI elements, and do application logic, etc. MSDN says that you can only access UI elements from UI thread. So, how can we delegate a task which will accessing UI element from a background thread? Since we need to update UI while doing background work, reporting progress or the completion of the work. SynchronizationContext can help us.

UI thread works on a work item queue mode. All works queued in a queue. UI thread select a work item which has the highest priority in the queue when the thread finished a work item. Because work items will be executed within UI thread, a big work item can block UI operations. So make work items small enough, don't do heavy work in it. That's why we need a background worker thread.

SynchronizationContext provides Post() and Send(). You can call them from your background thread, and delegate methods passed to them will be executed within UI thread. Delegate method passed to Post() will be executed asynchronously, while to Send(), the caller of Send() will wait until the delegate method returned.

DIG DEEPER: We can imaging the internal work of Post() or Send(), they will obtain the lock of the queue (work item queue) first, then insert the work item to the queue, and then release the lock.

Please find sample code at the end of this article. Firstly, we create a background thread in Window1() which is the initialization method of the application. We use the BeginInvoke() of the delegate type HeavyBackgroundWorkDelegate. The background thread will be created and started. AsyncCallback() is the handler of completion of background thread. Please note, it will be called from the background thread. In HeavyBackgroundWork(), the entry of background thread, we access UI element by synchronizationContext.Post().

From the debug ourput of the application, we can see, AsyncCallback is in the same thread as background, while UI thread is a different thread.

-------------------
UI thread: 10
Background thread: 6
HeavyBackgroundWork finished.
AsyncCallback is called from thread: 6
----------------------


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Threading;
using System.Diagnostics;

namespace Async_SynchronizationContext
{
///
/// Interaction logic for Window1.xaml
///
public partial class Window1 : Window
{
private delegate void HeavyBackgroundWorkDelegate();
private SynchronizationContext synchronizationContext;

private void HeavyBackgroundWork()
{
Debug.WriteLine("Background thread: " + Thread.CurrentThread.ManagedThreadId);

// To simulate a time-cost work.
Thread.Sleep(1000);

synchronizationContext.Post(new SendOrPostCallback(delegate(object param)
{
// We have to access TextInfo by synchronizationContext, otherwise
// you will meet an exception.
TextInfo.Text = "HeavyBackgroundWork() has been finished.";
}),
null);

Debug.WriteLine("HeavyBackgroundWork finished.");
}

public void AsyncCallback(IAsyncResult ar)
{
// This method will be called from HeavyBackgroundWork thread, not UI thread.
Debug.WriteLine("AsyncCallback is called from thread: " + Thread.CurrentThread.ManagedThreadId);
}

public Window1()
{
InitializeComponent();

synchronizationContext = SynchronizationContext.Current;

Debug.WriteLine("UI thread: " + Thread.CurrentThread.ManagedThreadId);
HeavyBackgroundWorkDelegate backgroundWork = new HeavyBackgroundWorkDelegate(HeavyBackgroundWork);
backgroundWork.BeginInvoke(AsyncCallback, null);
}
}
}

Monday, August 11, 2008

My public key

If you have something secrete for me, please use this key:


pub 1024D/01531EA3 2/27/2007 Shaking Sun (Sun Shaking GENUINE)
Primary key fingerprint: 9C22 753B 4BB0 1740 91F7 D933 BCB3 C63A 0153 1EA3

-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v1.4.7 (MingW32) - WinPT 1.2.0

mQGiBEXjoIkRBACjQiSDX9+YKW+p2jMAF1aZmFyFU67JKJ1Q1jMnZ8yTyK0iioYL
AUsW1Gye18yn5QLlyI38KN6mtO9Zb9SElyGsBjx0gfiarD1E8pHK7whFq+ZrY5WD
wdYsnXOKtR1LE/triPCtypBZRTGZqqJnAjlycopIYX2esnj+Q7U42JPP/wCg3Bm6
B2iSJLC/NolfvkyqsuNyObED/3FSXlz87V5Ve+A53G2jzHD3xb0fFyVvrCAlVcfp
w3jqRCDpqNnhEWfRF82eymhENSDJ5LLaXGvXk/FP0i6delbzoqyyZaTa8MieI9Gi
QwtZwqH/5VtxR859qaITfEeigYaEbQYg5uQLPef26ginwdu3amKWWEt33pla8ojG
crcBBACU3YfwJNttscCNQqMt0SgKdPrYL9/9/7j7cIpxhG/Tcx1WW/QV7/5O9GoH
iwN5T77ZbQen1SIVwbpr9B8fUgJ0ZiDZ//o4YaAmrd9u5ld1EnL2wpNVo2rBNHi/
BmBy6CNiGf4BTiJChPQxKm2QXjr7FY67w6eseY5oJDVDlIAyIrQ4U2hha2luZyBT
dW4gKFN1biBTaGFraW5nIEdFTlVJTkUpIDxzdW5zaGFraW5nQGdtYWlsLmNvbT6I
YAQTEQIAIAUCReOgiQIbAwYLCQgHAwIEFQIIAwQWAgMBAh4BAheAAAoJELyzxjoB
Ux6jz6YAn1tpRq4xZcin2cvCC6CdblXZ29O+AKDEdiBcSZx5udSw3/X1bq7NbRoU
WbkCDQRF46CVEAgAxohTys13AkSt000PCBy17NtV5vdCChmL/hrldfRZD9kurw4K
3ixfOcDZXQRmYE1BcQabJwntNPE090zLy84hM1wRc4aNW6iC2Ap1hHI4ZOntlbYd
ElShw7IDaM6+CXEiAnjG8kGldWD4zoCCBCSmSOHrSUDoBvY1UMPiZSUw+LYFh5Pw
hAyfr8gM8vc6oKy72/CSq1SdkA8YfylU90Wbka7xJbIKmTuFzqfJ5PGQ2c9yRecH
M/iuZ6lbSamPPecqw204HMYkTr6eRAGnQkCLx1fQeR3GlXi76OWg5WOFJ6dQAT0+
dsPsWY35F9vd/DbYlCJdbV4h2Fnl7l8MlMU7wwADBQf/Ym0821Qki6IfYMZ3cWR7
3gf6ClE7WstFBtSQF0fDAGqlBxzRGeuV4x75froPl09gxar8BUsYywh+1CuMr8Ur
VgAu3bWbWaJV+QJXF/EQfYHQdblcHEZ/aJbqeTX0Ot6cTeoDYRFRcVzibmipIEwk
c8hXedMungeiJUJuSHPRcT/Sa5v1xzk76KOdYVSZ1LZzSPps0QNRDE3C4+tG1oMO
3qhbc6n3Eekq5D2OjATmXnG4xBtwYq2KqRgaKbzOrUlDI0HImk3EHFxeDkZCE8VQ
dqF4iJcUXsdAiv3J26K0JSVPh0JPKTGHk9j/GIH2v2KXEmGliXlYGPZdEQXQVk1I
vohJBBgRAgAJBQJF46CVAhsMAAoJELyzxjoBUx6je7YAn2hZnY0TvjjxKfVAwxPR
zfvL4nSWAKCurJt4XCZkztb9orVulOPkv7kzXQ==
=+pwC
-----END PGP PUBLIC KEY BLOCK-----