Alik Levin's

Clarity, Technology, and Solving Problems | PracticeThis.com 

June, 2008

  • Alik Levin's

    Improve ASP.NET Performance With Multithreading Using Thread Or ThreadPool Objects

    • 8 Comments

    Your ASP.NET application performs slower than expected? How many times do you access your database for each request? Did you use SQL Profiler to find out? Did you know that chatty database access is one of the most common performance sins (this is my own observation)?

    Want to improve? - Reduce the number of database queries.

    Not an option? - You may want using multithreading. Proceed with caution.

    Real world scenario

    You need to run 3 independent heavy database queries – Q1, Q2, Q3. You need to use the result from the three – Result = Q1+Q2+Q3. You never know the order in which each query completes. It can be: Q1Q2Q3, Q1Q3Q2, Q2Q3Q1, Q2Q1Q3, Q3Q2Q1, Q3Q1Q2. Missed any combination? Run one after another would create significant latency. Running each on its own thread would create latency of Max(Q1,Q2,Q3) which is less that sum of the three.

    How to run each query on its own thread and wait for the completion for each one?

    Using Thread object

    The following code spawns three threads and waits for all to join, and then use the results from each.

       1: Thread t1 = new Thread(DoWork1);
       2: t1.Start();
       3:  
       4: Thread t2 = new Thread(DoWork2);
       5: t2.Start();
       6:  
       7: Thread t3 = new Thread(DoWork3);
       8: t3.Start();
       9:  
      10: t1.Join(1000);
      11: t2.Join(1000);
      12: t3.Join(1000);
      13:  
      14:  
      15: Label1.Text = (i1 + i2 + i3).ToString();

    See any issues using it in ASP.NET? One caveat though, creating threads manually like this may cause performance hit as it involves context switching – CPU consuming operation. Consider using ThreadPool object.

    Using ThreadPool object (preferred, but are you using COM?)

    ThreadPool is preferred as it already has live threads allocated for you. No need to ask for a favor from CPU in the moment of truth. Here is the code:

       1: public partial class _Default : System.Web.UI.Page
       2: {
       3:     int i1 = 0;
       4:     int i2 = 0;
       5:     int i3 = 0;
       6:  
       7:     WaitHandle[] waitHandles = new WaitHandle[]
       8:         { 
       9:             new ManualResetEvent(false),
      10:             new ManualResetEvent(false),
      11:             new ManualResetEvent(false) 
      12:         };
      13:  
      14:  
      15:     protected void Page_Load(object sender, EventArgs e)
      16:     {
      17:  
      18:     }
      19:  
      20:     protected void Button1_Click(object sender, EventArgs e)
      21:     {
      22:         Stopwatch sw = new Stopwatch();
      23:  
      24:         sw.Start();
      25:  
      26:         WaitCallback method1 = new WaitCallback(DoWork1);
      27:         bool isQueued1 = ThreadPool.QueueUserWorkItem(method1, waitHandles[0]);
      28:  
      29:         WaitCallback method2 = new WaitCallback(DoWork2);
      30:         bool isQueued2 = ThreadPool.QueueUserWorkItem(method2, waitHandles[1]);
      31:  
      32:         WaitCallback method3 = new WaitCallback(DoWork3);
      33:         bool isQueued3 = ThreadPool.QueueUserWorkItem(method3, waitHandles[2]);
      34:  
      35:         if (WaitHandle.WaitAll(waitHandles, 5000, false))
      36:             Label1.Text = (i1 + i2 + i3).ToString();
      37:         else
      38:             Label1.Text = "Problem";
      39:  
      40:     }
      41:  
      42:      void DoWork1(object state)
      43:     {
      44:         int.TryParse(TextBox1.Text, out i1);
      45:  
      46:         //HEAVY QUERY GOES HERE. Sleep is for the demo only! Remove it!
      47:         Thread.Sleep(i1);
      48:         ManualResetEvent mre = (ManualResetEvent)state;
      49:         mre.Set();
      50:  
      51:     }
      52:  
      53:      void DoWork2(object state)
      54:     {
      55:         int.TryParse(TextBox2.Text, out  i2);
      56:  
      57:         //HEAVY QUERY GOES HERE. Sleep is for the demo only! Remove it!
      58:         Thread.Sleep(i2);
      59:         ManualResetEvent mre = (ManualResetEvent)state;
      60:         mre.Set();
      61:     }
      62:  
      63:      void DoWork3(object state)
      64:     {
      65:         int.TryParse(TextBox3.Text, out  i3);
      66:  
      67:         //HEAVY QUERY GOES HERE. Sleep is for the demo only! Remove it!
      68:         Thread.Sleep(i3);
      69:         ManualResetEvent mre = (ManualResetEvent)state;
      70:         mre.Set();
      71:     }
      72:  
      73: }

    Caveat

    If your function calls on COM object – avoid using ThreadPool, its Apartment model is not compatible with COM and it cannot be changed. More info - Pitfalls With .Net Multithreading And COM Objects – Threads Must Have Compatible Apartment Models (MTA vs. STA).

    Another point to call out is that performance improvements based on multithreading is subject to amount of available CPU’s. Test your solution first!

    This post was written with help from Lior, MCS Israel Architect.

    Download sample project with the code from my SkyDrive:

    Enjoy.

  • Alik Levin's

    Pitfalls With .Net Multithreading And COM Objects – Threads Must Have Compatible Apartment Models (MTA vs. STA)

    • 1 Comments

    Be alert when implementing multithreading .Net in conjunction with COM objects. Thread apartment models matter.

    .Net threads have Multi Threaded Apartment (MTA) model by default. COM objects have Single Thread Apartment (STA). Calling on COM objects on .Net threads that you spawn may cause unpredicted result.

    Multithreading in .Net is easily implemented based on either Thread or ThreadPool objects. Thread.Start() method spawns new thread which has Multi Threaded Apartment (MTA) model . ThreadPool.QueueUserWorkItem(myMethod) queues myMethod to be executed on available thread managed by it.

    Thread object and COM

    When spawning your own threads with Thread.Start() set Thread’s apartment model to STA if you plan calling COM object on it:

       1: Thread t = new Thread(DoWork);
       2: t.SetApartmentState(ApartmentState.STA);
       3: t.Start();

    Note, spawning your own threads with Thread.Start() utilizes resources – mainly CPU, spawning too much threads may cause performance hit. Consider using ThreadPool object.

    ThreadPool object and COM

    All threads managed by ThreadPool objects are MTA threads. Apartment model of ThreadPool’s threads cannot be changed. Do not call COM objects on ThreadPool’s threads.

    Possible designs and solutions

    • ASP.NET. Calling COM objects in ASP.NET pages configure AspCompat=true:

    “Most COM components will work with ASP.NET…”

    “…The AspCompat attribute forces the page to execute in STA mode. The runtime throws an exception if the compatibility tag is omitted and an STA component is referenced on the page.”

    • Client side (like WinForms)multithreading. Use ThreadPool with its MTA threads. Spawn another thread with Thread.Start() setting its apartment model to STA.
    • Custom server side multithreading. I’d consider using Remoting infrastructure. In case your server application should serve messages that are not covered by Remoting’s built in mechanisms I’d consider building my own Channel sink.
    • And the last but not the least. Do not forget to call Marshal.ReleaseComObject(obj) to dispose the COM objects created on your the threads.

    Related materials

  • Alik Levin's

    Windows Live Writer (WLW) Plugin For Post Templates - Boost Your Blogging Productivity Instantly

    • 2 Comments

    Using Windows Live Writer (WLW)? Writing posts takes you more time than you would like to invest? Looking for a ways to streamline your blogging? Want to be more productive and more efficient with blogging?

    Try using Blog Post Template Plugin For Windows Live Writer.


    Video: Blog Post Template Plugin for Windows Live Writer

    Music by zeropage

    I’ve created it for my personal needs and I thought it’d be good idea to share it with the blogoworld.

    Installing the Plugin

    No MSI mess – pure XCOPY “install”. Two files:

    • PracticeThis.dll. It is the plugin itself that goes to Plugins folder.
    • Template.txt. This is the template. It can be edited in any text editor Like Notepad or in WLW itself. The details are here.

    Using the Plugin

    To use the plugin just pick it from Insert menu and you are done. It is like Word template for resume or Visual Studio template for Web Projects. How would look your day if you’d need to write resume from scratch or Web Projects in Notepad?

     image

    Editing your template

    To edit the default tempalte that comes with the downlad just open template.txt file that is located in WLW’s installation folder and change it to whatever you like it. I use WLW to edit is then open Source pane, copy and paste it into Template.txt.

    Try it

    Go ahead and give it a shot – try Blog Post Template Plugin For Windows Live Writer. Drop me a line in case you like it, drop me another in case you don’t. Let me know what should I improve to make sure you love it.

    Have fun. BTW, this post was created with the plugin.

    Security Warning

    Do not accept templates from untrusted sources - of course you can trust me…:). WLW renders the text as is so if someone sends you template with JavaScript in it, WLW will happily execute it. The code might pop up funny balloons, or do bad things. To make sure the Template is harmless open it in Notepad and look for “Script” blocks or funky URL’s (search for “href”).

Page 1 of 2 (5 items) 12