5,550,131 members and growing! (19,148 online)
Email Password   helpLost your password?
General Programming » Threads, Processes & IPC » Multi-threading     Intermediate

Converting single threaded C# class to multithreaded one

By Alex_1

The article desribribes the technique and the software tool for conversion of single threaded classes to multithreaded.
C#, .NET, Win2K, WinXP, Win2003, Windows, Visual Studio, Dev

Posted: 12 Oct 2003
Updated: 24 Oct 2003
Views: 43,648
Bookmarked: 19 times
Announcements
Want a new Job?



Search    
Advanced Search
Sitemap
14 votes for this Article.
Popularity: 2.97 Rating: 2.59 out of 5
5 votes, 35.7%
1
1 vote, 7.1%
2
1 vote, 7.1%
3
0 votes, 0.0%
4
7 votes, 50.0%
5

Sample Image - AmThreader.png

Terms definition

Straight after the publishing of this article, I received criticism from number of readers including the reader dog_spawn: "How can you ever call a class 'single threaded' or 'multithreaded'? I have programmed using threads for years (C++, Java and recently C#) but your article makes absolutely no sense to me". In fact, he is right - there is no such a thing as single threaded or multithreaded class, on the other hand, I do not have better name for the construction I have created. Perhaps, I can name it a Thread Isolator. To avoid further misunderstanding, I would like to make clear that AmThreader is a code generator that creates a class wrapper for any .NET class (not necessarily C#), and any call of wrapper class method will be translated to original class call but in a separate thread.

Introduction

All of us use computers today. You start an application, click controls, and at some moment it stops to respond. Well, the most probable cause of it is that the main thread is unable to exit (or continue) for some reason. What reason? The reason in fact, can be any: Input-Output cannot complete, some network problems, internal application error, and the thread went into endless loop etc. Very annoying situation, indeed. How can this problem be resolved? The only way to create a responsive, and in a certain sense, robust application is to use multithreading. However, everything is at cost because the development of multithreaded applications is more complicated. It involves threads synchronization, considerably more complicated debugging, and so on. That is why even reputable companies still use single threading. So, how to simplify the development of multithreaded software?

Code Generator

The answer is AmThreader - the code generator. It converts any single threaded .NET class to multithreaded one. How it works?

Let's look first into the stages of the development of simple multithreaded applications. What are the components of multithreading? Nothing in fact is really special:

  1. Temporary variables for holding and passing the parameters from one thread to another.
  2. A new thread (worker thread).
  3. Methods to invoke certain methods in the worker thread.
  4. A loop that runs in the worker thread.
  5. A bunch of enums, switches and cases that control the work flow in the worker thread.
  6. Wait procedure (or extra thread).
  7. Threads synchronization components (depend on OS and used language).
  8. Exception handling.

For instance, we have a class ClassA that has the method DoSomething() which may not return.

using System;

namespace SomeNameSpace 
{ 
public class ClassA 
    { 
        public ClassA() 
        { 
        }
        public void DoSomething(int b) 
        {
            // do something 

        } 
    } 
}

Let's convert it to a multithreaded one in order to make the application safe and friendly. Speaking in C# terms, this new class can be represented with a snippet below:

namespace SomeNameSpace 
{ 
    public class __ClassA : IDisposable
    { 
        private enum Commands_enums 
        { 
            ClassA__enum_0, 
            DoSomething__enum_1
        } 
        private Commands_enums CurrCommand; 
        private bool bError; 
        private bool ThreadStop; 
        private ManualResetEvent EventStart = new ManualResetEvent(false); 
        private object[] inParams = new object[100]; 
        private object ValueParam; 
        private Thread fThread; 
        private int nTimeOut = 10000; 
        private int CycleTime = 15; 
        private ClassA ThreadObjectInstance; 

        public __ClassA() 
        { 
            StartThread(); 
            CurrCommand = Commands_enums.ClassA__enum_0; 
            WaitForComplete(); 
        } 

        public void DoSomething(int b) 
        { 
            inParams[0] = b; 
            CurrCommand = Commands_enums.doSomething__enum_1; 
            WaitForComplete(); 
        } 

        private void Worker_Thread_01() 
        { 
            while(!ThreadStop) 
            { 
                EventStart.WaitOne(); 
                switch(CurrCommand) 
                { 
                    case Commands_enums.ClassA__enum_0: 
                      ThreadObjectInstance = new ClassA(); 
                      break; 

                    case Commands_enums.DoSomething__enum_1: 
                      ThreadObjectInstance.DoSomething((int)inParams[0]); 
                      break; 
                    default: 
                      break; 
            } 

        bStopWaiting = true; 
        CurrCommand = Commands_enums.wait_command__enum_; 

        EventStart.Reset(); 

        } 
    } 

    private void StartThread() 
    { 
        ThreadStart thr_start_func = new ThreadStart (Worker_Thread_01); 
        fThread = new Thread (thr_start_func); 
        fThread.Name = "Worker_Thread_01"; 
        fThread.Start (); //starting the thread 

    } 

    private void WaitForComplete() 
    { 
        int ElapsedTime = 0; 
        EventStart.Set(); 
        while(!bStopWaiting) 
        { 
            //** Place for event processing 

            ElapsedTime += CycleTime;
            if (ElapsedTime > nTimeOut) 
            { 
                    bStopWaiting = true; 
                    throw new Exception("Wait TimeOut"); 
            } 
            Thread.Sleep(CycleTime); 
        } 
    } 

    public void Dispose()
    {
        bStopWaiting = true;
        ThreadStop = true;
        EventStart.Set();
    }

}

The snippet above is the simplest ,though complete multithreaded class. Yes, it is simple, but what can it do? Not much, the only thing it can do well is wait for the completion of the DoSomething() method and announce its failure if the worker thread sticks in it. Even with this limited functionality, it is useful. The application can always be terminated gracefully, although the main thread is still frozen during the execution of DoSomething(). However, for event driven applications (all GUI or WinForms), there is a trick to overcome that unpleasant effect. We only need to place Application.DoEvents() inside Wait loop. The interface will still be responding during DoSomething(). The code line Thread.Sleep(CycleTime) controls the responsiveness of the interface. The smaller CycleTime, the more responsive the interface is, on the other hand, it uses more CPU resources.

How can the new class __ClassA be used? The same way the original class is.

For example: the original class ClassA can be used in the way something like this:

try 
{
    ClassA MyClassA = new ClassA();
    MyClassA.DoSomething(5); 
} 
catch(Exception ex) 
{ 
    MessageBox.Show(ex.Message); 
}

How to use the new class? Well, almost the same. The only difference is that the class must be destroyed explicitly calling Dispose(), otherwise the application will never end with worker thread running.

try
{ 
    __ClassA MyClassA = new __ClassA();
    MyClassA.DoSomething(5); 
    MyClassA.Dispose();
}
catch(Exception ex)
{
    MessageBox.Show(ex.Message);
}

So, what is so special in __ClassA and how can it be derived (not in the sense of inheritance) from ClassA? Absolutely nothing and this operation can be automated completely.

Using Reflection, AmThreder generates a new class from the Assembly with the same method names, indexers, and properties as the original class has. Only public methods are processed, even the source code of the original class is no longer needed.

Where to download

To download fresh version of AmThreader, visit http://www.amplefile.com/, "downloads" page. AmThreader constantly monitors if fresh version is available and will notify you.

History

  • Initial version - 1.0.2.0
  • Current version - 1.0.4.0

License

For independent developers, AmThreader is free. However it has to be registered.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Alex_1



Occupation: Web Developer
Location: Australia Australia

Other popular Threads, Processes & IPC articles:

Article Top
Sign Up to vote for this article
You must Sign In to use this message board.
FAQ FAQ Noise ToleranceSearch Search Messages 
 Layout  Per page   
 Msgs 1 to 8 of 8 (Total in Forum: 8) (Refresh)FirstPrevNext
Subject  Author Date 
GeneralNice...memberro_angel_bv6:55 15 Apr '04  
GeneralStarting a new thread in context of constructormemberENewton12:02 1 Dec '03  
GeneralDon't get itmemberdog_spawn10:22 13 Oct '03  
GeneralRe: Don't get itmemberdog_spawn15:22 13 Oct '03  
GeneralRe: Don't get itmemberNormski21:48 14 Oct '03  
GeneralRe: Don't get itsussAnonymous20057:23 15 Apr '04  
GeneralRe: Installation not workingmemberrstaylor6:13 16 Oct '03  
GeneralRe: Installation not workingmemberAlex_112:38 16 Oct '03  

General General    News News    Question Question    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

PermaLink | Privacy | Terms of Use
Last Updated: 24 Oct 2003
Editor: Smitha Vijayan
Copyright 2003 by Alex_1
Everything else Copyright © CodeProject, 1999-2008
Web17 | Advertise on the Code Project