Friday, January 16, 2015

X++ Multi-Threading & Sessions

In this blog, I'm going to discuss briefly about multi threading in X++ (Microsoft Dynamics Axapta)

To start with, suppose you needed to process 1000 records. Each record has 100 columns to be processed. Each column takes 1 second to process. In total you would require 100 X 1 seconds=100 Seconds to execute 1 record. This translates to 100 X 1000=100000seconds to execute 1000 records.
 This is because, by default, tasks are executed using the traditional single threaded apartments.

Now if you were to apply multi-threading, the execution time would greatly be reduced. This is because, you can  assign different threads at different levels of processing/execution. Suppose we want to introduce a new thread to handle each record, ie 1000 threads. Each thread would take 100seconds to execute. NOTE: The multiple threads do not wait for each other, rather, they all execute simultaneously. Therefore, the total time to execute 1000 records can be reduced from  100000seconds to 100seconds, depending on the power of your processors.

Below is a sample X++ code which demonstrates multi threading in Dynamics AX. The code has been written and tested in Microsoft Dynamics AX 2012 R2.

//this is the parent thread
public void ImportData()
{
    container           record;
    Dialog              dialog  = new Dialog();
    DialogField         dialogField;
    ExecutePermission   perm;
    Thread              myThread;
    str                 Delimiter = ",";

    try
    {
        dialogField=dialog.addField(
        extendedTypeStr(FilenameOpen),
        "Select "+enum2str(DataToImport)+" File To Import",
        "Please select the file containing "+enum2str(DataToImport)+" data."
        );

        dialog.caption("Import "+enum2str(DataToImport)+" Details");
        dialog.filenameLookupFilter(['csv','*.csv']);

        if(!dialog.run())
            return;

        [filePath, fileNameOnly, type] = fileNameSplit(dialogField.value());

        importFile = new AsciiIo(dialogField.value(), 'R');

        if((!importFile) || (importFile.status() != IO_Status::Ok))
        {
            warning("Error in opening "+enum2str(DataToImport)+" file");

            throw(Exception::Error);
        }

        importFile.inFieldDelimiter(Delimiter);

        if((!importFile) || (importFile.status() != IO_Status::Ok))
        {
            warning("Error in opening log "+enum2str(DataToImport)+" file");
            throw(Exception::Error);
        }
        record = importFile.read();

        while(importFile.status() ==  IO_Status::Ok)
        {
            record = importFile.read();
            if(!record) break;

            perm = new ExecutePermission();
            if (!perm)return;
            perm.assert();

            myThread = new Thread();
            myThread.setInputParm([record]);
            if (myThread)
            {
                myThread.removeOnComplete(true); 
                ttsBegin;       

                    case LMImportFormats::PersonalDetails:
                        myThread.run( classnum(MyClassXXX), staticMethodStr(MyClassXXX,MyMethodYYY));
                       
                ttsCommit;
            }
            CodeAccessPermission::revertAssert();

            totalRecords++;
        }

        info(strFmt("Total "+enum2str(DataToImport)+" Records Processed = %1",totalRecords));
    }
    catch(Exception::Error)
    {
        Throw(Exception::Error);
    }
}


//this is the child thread, of which multiple instances are created, to process the records
//NOTE: For each record, this method is called
private static void MyMethodYYY(Thread myThread)
{
    container threadRecord;
    container record_;
    str myVariable;
    try
    {
        threadRecord=myThread.getInputParm();
        record_=conPeek(threadRecord,1);
       
        myVariable = strLRTrim(conPeek(record_,1));
       //todo: add your logic hapa kisha furahia coding
       //todo: add your logic hapa kisha furahia coding
       //todo: add your logic hapa kisha furahia coding
       //todo: add your logic hapa kisha furahia coding
       //todo: add your logic hapa kisha furahia coding
    }
    catch(Exception::Error)
    {
        info(strFmt("Kuna shida na code yako."));
    }
}







**************HAPPY CODING 2015**************