Thread: Japo's code
View Single Post
Old 26-09-2010, 01:17 PM   #2
Japo
Autonomous human
 
Japo's Avatar


 
Join Date: Mar 2006
Location: ,
Posts: 4,613
Default xThreading 1

Something more useful today. My preferred programming framework is .NET, and my preferred language C#. With .NET it's very easy to take do multi-threading, so you can perform tasks in parallel, taking advantage of modern CPUs; and most important, so your user interface doesn't stop responding whenever you have to perform a task that takes some time, instead you can perform it in the background while the UI thread goes on in parallel.

Even though the .NET class library does most of the work, I made a little library to take care of some common tasks when multi-threading and cross-threading, I called it "xThreading".

First it has a simple extension method to make thread-safe calls to Controls (actually any ISynchronizeInvoke) as detailed here. The arguments it takes are a delegate to the method that you want to run in the Control's thread, and the parameters, if any. (I provided an overload for the frequent case when the method has no parameters nor return value--is a System.Action delegate--because just "Delegate" isn't strongly typed (System.Delegate is abstract) and you'd need an instantiated delegate every time you used this, but the compiler can't infer it because Delegate is abstract. You could make additional overloads for other recurrent situations.)

Code:
using System;
using System.ComponentModel;

namespace SEADM.xThreading
{
   public static class xExtensions
   {
      public static object xInvoke(this ISynchronizeInvoke thread,
         Delegate method, params object[] args)
      {
         if(args.Length == 0) args = null; // if the Delegate takes no arguments
            // we must pass null to ISynchronizeInvoke.Invoke() or Delegate.DynamicInvoke(),
            // not an empty array.

         if(thread.InvokeRequired)
            return thread.Invoke(method, args);
         else
            return method.DynamicInvoke(args);
      }

      // Handy overload:
      public static void xInvoke(this ISynchronizeInvoke thread,
         Action method)
      { xInvoke(thread, method, null); }
   }
}
So let's say you have a reference to a Form called "frm" for example, and you want to close it from a different thread from where the Form was created. You can't call frm.Close() directly, you'll get an exception if debugging, and things will probably hang if you're running the program normally. Using the xInvoke() extension method you can write instead
Code:
frm.xInvoke( () => frm.Close() );
OK it's still verbose and clumsy and hard to look at. That's why this library defines some classes in addition to this method, that I'll post right away. But I still leave xInvoke() public, so I have flexibility to make the calls I want. Specially if you want to access several members of a Control in one go, since each cross-thread call is in principle bad for performance, it's best to bundle everything you want to do with the Control into a single Delegate, and just pass it to xInvoke():
Code:
frm.xInvoke( () =>
{
   frm.lbl.Text = "Done! :)";
   frm.btn.Enabled = true;
   // etc...
} );
It's OK to access child Controls for example, provided they were created in the same thread as their Parent.
__________________
Life starts every day anew. Prospects not so good...

Last edited by Japo; 05-01-2011 at 06:38 PM. Reason: added overload
Japo is offline                         Send a private message to Japo
Reply With Quote