xlsgen > overview > Multi-core sample

xlsgen supports multiple threads, one thread per document. The age of multi-core computers is particularly well suited for maximizing the speed of xlsgen scenarios.

In the following is a rather simple piece of code run in multiple threads (the number of threads is evaluated from the actual physical hardware count), that adds pseudo-random content to a spreadsheet. It is written in Java, VB.NET, C# and C++ and is available in the /samples folder of the install.

Java code
class multicore_threads {

	public static class WorkerThread extends Thread {

		protected XlsEngine m_engine;
		protected boolean m_bStopped;
		protected String m_szFilename;

		WorkerThread(XlsEngine engine, String szFilename)
		{
			m_engine = engine;
			m_bStopped = false;
			m_szFilename = szFilename;
		}

		public boolean IsStopped()
		{
			return m_bStopped;
		}

		public void run()
		{
			XlsWorkbook wbk = m_engine.New(m_szFilename);

			XlsWorksheet wksht = wbk.AddWorksheet( "sheet1" );

			int cnt = 0;

			for (int r = 1; r < 40000; r++)
			{
				for (int c = 1; c <= 20; c++)
				{
					wksht.putLabel(r, c * 2 + 0, "Label");
					wksht.putNumber(r,c * 2 + 1, cnt++);
				}
			}

			wbk.Close();

			m_bStopped = true;

		}

		public void destroy()
		{
		}
	}

    public static void main(String[] args) {

        XlsEngine engine = new XlsEngine("./../../../xlsgen.dll");

        
        int nbThreads = Runtime.getRuntime().availableProcessors();

        System.out.println("multicore sample : " + nbThreads + " threads being created (one per core)");


        long startTicks = System.currentTimeMillis();

        List arrThreads = new ArrayList(nbThreads);

        for (int i = 0; i < nbThreads; i++)
        {
            WorkerThread t = new WorkerThread(engine, "myfile" + i + ".xls");
            t.start();

            arrThreads.add(t);
        }

        while (true)
        {
            try
            {
                Thread.sleep(100);
            }
            catch (InterruptedException e)
            {
            }

            boolean bAtLeastOneRunning = false;

            for (int i = 0; i < nbThreads; i++)
            {
                WorkerThread t = (WorkerThread) arrThreads.get(i);
                if (!t.IsStopped())
                {
                    bAtLeastOneRunning = true;
                    break;
                }
            }

            if (!bAtLeastOneRunning)
                break;
        }

        long endTicks = System.currentTimeMillis();

        long secs = (endTicks - startTicks) / 1000;

        System.out.println("test duration = " + secs + " seconds");

        BufferedReader br = new BufferedReader( new InputStreamReader( System.in ) ); 

        try
        {
            br.readLine();
        }
        catch(IOException e)
        {
        }

    }
}
VB.NET code

Imports System
Imports System.Threading
Imports xlsgen


Module Module1

    Public Class WorkerThread

        Protected m_engine As CoXlsEngine
        Protected m_szFilename As String
        Protected m_bStopped As Boolean

        Sub New(ByVal engine As CoXlsEngine, ByVal szFilename As String)
            m_engine = engine
            m_szFilename = szFilename
            m_bStopped = False
        End Sub

        Public Function IsStopped() As Boolean
            IsStopped = m_bStopped
        End Function

        Public Sub Run()

            ' code begins here
            '...

            Dim wbk As IXlsWorkbook
            wbk = m_engine.New(m_szFilename)

            Dim wksht As IXlsWorksheet
            wksht = wbk.AddWorksheet("sheet1")

            Dim cnt = 0

            For r = 1 To 40000
                For c = 1 To 20
                    wksht.Label(r, c * 2 + 0) = "Label"
                    wksht.Number(r, c * 2 + 1) = cnt
                    cnt = cnt + 1
                Next
            Next

            wbk.Close()

            m_bStopped = True

        End Sub

    End Class


    Public Class Program

        Public Shared Sub Main()

            Dim startTicks = DateTime.Now.Ticks

            Dim engine As CoXlsEngine = New CoXlsEngine

            Dim arrThreads As New List(Of WorkerThread)

            Dim nbThreads = System.Environment.ProcessorCount

            Console.WriteLine("multicore sample : " & nbThreads & " threads being created (one per core)")

            ' create worker threads
            '
            For i = 1 To nbThreads

                Dim t As New WorkerThread(engine, "myfile" & i & ".xls")

                arrThreads.Add(t)

                ThreadPool.QueueUserWorkItem( _
                    New WaitCallback(AddressOf ThreadProc), t
                    )
            Next

            Console.WriteLine("Worker threads started.")

            ' wait for all worker threads to end
            '
            While (True)

                Thread.Sleep(100)

                Dim bAtLeastOneRunning = False

                For Each t As WorkerThread In arrThreads
                    If (t.IsStopped() = False) Then
                        bAtLeastOneRunning = True
                        Exit For
                    End If
                Next

                If (bAtLeastOneRunning = False) Then
                    Exit While
                End If
            End While

            Dim endTicks = DateTime.Now.Ticks

            Dim elapsedSpan As TimeSpan = New TimeSpan(endTicks - startTicks)

            Console.WriteLine("test duration = " & elapsedSpan.TotalSeconds & " seconds.")

            Console.WriteLine("Main thread exits.")
            Console.ReadLine()

        End Sub

        ' This thread procedure performs the task.
        Shared Sub ThreadProc(ByVal stateInfo As Object)

            Dim t As WorkerThread
            t = stateInfo
            t.Run()

        End Sub
    End Class

     _
    Sub Main()
        Program.Main()
    End Sub

End Module

C# code

    class WorkerThread
    {
        protected CoXlsEngine m_engine;
        protected String m_szFilename;
        protected bool m_bStopped;

        public WorkerThread(CoXlsEngine engine, String szFilename)
        {
            m_engine = engine;
            m_szFilename = szFilename;
            m_bStopped = false;
        }

        public bool IsStopped()
        {
            return m_bStopped;
        }

        public void Run()
        {
            // code begins here
            //...

            IXlsWorkbook wbk = m_engine.New(m_szFilename);

            IXlsWorksheet wksht = wbk.AddWorksheet( "sheet1" );

            int cnt = 0;

            for (int r = 1; r < 40000; r++)
            {
                for (int c = 1; c <= 20; c++)
                {
                    wksht.set_Label(r, c * 2 + 0, "Label");
                    wksht.set_Number(r, c * 2 + 1, cnt++);
                }
            }

            wbk.Close();

            m_bStopped = true;
        }

    }

    class Program
    {
        static void Main(string[] args)
        {
            long startTicks = DateTime.Now.Ticks;

            CoXlsEngine engine = new CoXlsEngine();

            List<WorkerThread> arrThreads = new List<WorkerThread>();

            int nbThreads = System.Environment.ProcessorCount;

            Console.WriteLine("multicore sample : " + nbThreads + " threads being created (one per core)");

            // create worker threads
            //
            for (int i = 0; i < nbThreads; i++)
            {
                WorkerThread t = new WorkerThread(engine, "myfile" + i + ".xls");
                arrThreads.Add(t);
                ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadProc), t);
            }
            
            Console.WriteLine("Worker threads started.");

            // wait for all worker threads to end
            //
            while (true)
            {
                Thread.Sleep(100);

                bool bAtLeastOneRunning = false;

                for (int i = 0; i < nbThreads; i++)
                {
                    WorkerThread t = arrThreads.ElementAt(i);
                    if (!t.IsStopped())
                    {
                        bAtLeastOneRunning = true;
                        break;
                    }

                }

                if (!bAtLeastOneRunning)
                    break;
            }

            long endTicks = DateTime.Now.Ticks;

            TimeSpan elapsedSpan = new TimeSpan(endTicks - startTicks);

            Console.WriteLine("test duration = " + elapsedSpan.TotalSeconds + " seconds.");

            Console.WriteLine("Main thread exits.");
            Console.ReadLine();
        }

        // This thread procedure performs the task.
        static void ThreadProc(Object stateInfo)
        {
            WorkerThread t = (WorkerThread)stateInfo;
            t.Run();
        }

    }

C/C++ code

    ::CoInitialize(NULL);

    SYSTEM_INFO sysinfo;
    ::GetSystemInfo( &sysinfo );

    int nbThreads = sysinfo.dwNumberOfProcessors;

    TCHAR sztmp[128];
    _stprintf(sztmp,"multicore sample : %d threads being created (one per core)\r\n", nbThreads);
    OutputDebugString(sztmp);

    LPUNITHREAD* coll = new LPUNITHREAD[nbThreads];

    DWORD dwStart, dwEnd;

    {
        xlsgen::IXlsEnginePtr engine( __uuidof(xlsgen::CoXlsEngine) );

        dwStart = ::GetTickCount();

        int i;
        for (i = 0; i < nbThreads; i++)
        {
            TCHAR szFilename[MAX_PATH];
            sprintf(szFilename, "%s%d%s", "myfile", i, ".xls");
			
            CUnitThread* u = new CUnitThread();
            if (u)
            {
                u->Start(engine, i, szFilename);
                coll[i] = u;
            }
        }

        while (1)
        {
            ::Sleep(100);

            bool bAtLeastOneRunning = false;

            for (i = 0; i < nbThreads; i++)
            {
                if (!coll[i]->IsStopped())
                {
                    bAtLeastOneRunning = true;
                    break;
                }
            }

            if (!bAtLeastOneRunning)
                break;
        }

        dwEnd = ::GetTickCount();

        for (i = 0; i < nbThreads; i++)
        {
            delete coll[i];
        }
        delete [] coll;
    }

    DWORD dwElapsed = dwEnd - dwStart;
    DWORD secs = dwElapsed / 1000;


    _stprintf(sztmp,"test duration = %d seconds\r\n", secs);
    OutputDebugString(sztmp);

    ::CoUninitialize();

    return 0;


--- unitthread.h ---


#pragma once

class CUnitThread;
typedef CUnitThread* LPUNITHREAD;

class CUnitThread
{

    // Members
    //
protected:
    int m_nCnt;
    TCHAR m_szFilename[MAX_PATH];
    xlsgen::IXlsEnginePtr m_engine;

    // thread mgt members
    DWORD m_ThreadId ;	// ID of thread
    HANDLE m_hThread ;	// Handle to thread
    HANDLE m_hEvent;	// Handle to event
    HANDLE m_hComponentReadyEvent ;	// Event signals thread to continue.

    bool m_bStopped;


    // Constructor
    //
public:
    CUnitThread();
    virtual ~CUnitThread();


    // Methods
    //
public :
    BOOL Start(xlsgen::IXlsEnginePtr &engine, int id, LPSTR filename);
    void Stop();

    void Increase();

    // Accessors
    //
public:
    int GetCnt();
    bool IsStopped();

    // Internal
    //
protected:
    BOOL StartThread(HANDLE hEvent) ;	// Create and start the thread.
    BOOL IsThreadStarted() ; 	// Current thread status
    static DWORD WINAPI RealThreadProc(void* pv) ;	// Thread procedure
    DWORD ClassThreadProc() ;	// Member thread procedure
    BOOL WaitWithMessageLoop(HANDLE hEvent) ;	// Wait for an event, but process window messages.
	

};


--- unitthread.cpp ---

#include "unitthread.h"

CUnitThread::CUnitThread()
{
    m_ThreadId = 0 ;	
    m_hThread  = NULL ;
    m_hComponentReadyEvent = m_hEvent = NULL ;

    m_bStopped = false;

    m_nCnt = 0;
}

CUnitThread::~CUnitThread()
{
    if (m_hThread) ::TerminateThread(m_hThread,0);
}




BOOL CUnitThread::Start(xlsgen::IXlsEnginePtr &engine, int id, LPSTR filename)
{
    _tcscpy(m_szFilename, filename);
    m_nCnt = id;
    m_engine = engine;

    return StartThread(NULL);
}

void CUnitThread::Stop()
{
}



int CUnitThread::GetCnt()
{
    return m_nCnt;
}

void CUnitThread::Increase()
{
    m_nCnt++;
}

bool CUnitThread::IsStopped()
{
    return m_bStopped;
}

///////////////////////////////////////////////////////////
//
// StartThread
//   - Create and start the thread.
//
BOOL CUnitThread::StartThread(HANDLE hEvent) 
{
    if (IsThreadStarted())
    {
        if (m_hThread) ::TerminateThread(m_hThread,0);
        m_hThread = NULL;
    }

    // Create the thread.
    m_hThread = ::CreateThread(NULL,              // Default security
	                           0,                 // Default stack size
	                           RealThreadProc,
	                           (void*)this,
	                           CREATE_SUSPENDED,  // Create the thread suspended.
	                           &m_ThreadId) ;     // Get the Thread ID.
    if (m_hThread == NULL)
    {
        DWORD nError = ::GetLastError();
        return FALSE ;
    }

    // Create an event for the thread to signal when it is finished. 
    m_hComponentReadyEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL) ;
    if (m_hComponentReadyEvent == NULL)
    {
        return FALSE ;
    }

    // store event handle passed by parent
    //
    m_hEvent=hEvent;

    // Thread was created suspended; start the thread.
    DWORD r = ResumeThread(m_hThread) ;

    // Wait for the thread to start up before we continue.
    WaitWithMessageLoop(m_hComponentReadyEvent) ;

    return TRUE;
}

	
///////////////////////////////////////////////////////////
//
// Current thread status
//
BOOL CUnitThread::IsThreadStarted()
{
    return (m_hThread != NULL) ;
}

///////////////////////////////////////////////////////////
//
// Thread procedure
//
DWORD WINAPI CUnitThread::RealThreadProc(void* pv) 
{
    CUnitThread* pApartment = reinterpret_cast<CUnitThread*>(pv) ;
    return pApartment->ClassThreadProc() ;
}

void increaseCnt(CUnitThread* t)
{
    t->Increase();
}

///////////////////////////////////////////////////////////
//
// Thread procedure
//
DWORD CUnitThread::ClassThreadProc()
{
    // Signal that we are starting.
    SetEvent(m_hComponentReadyEvent) ;

    // code begins here
    //...

    xlsgen::IXlsWorkbookPtr wbk;
    wbk = m_engine->New( _bstr_t(m_szFilename) );

    xlsgen::IXlsWorksheetPtr wksht;
    wksht = wbk->AddWorksheet( L"sheet1" );

    int cnt = 0;

    for (int r = 1; r < 40000; r++)
    {
        for (int c = 1; c <= 20; c++)
        {
            wksht->Label[r][c * 2 + 0] = L"Label";
            wksht->Number[r][c * 2 + 1] = cnt++;
        }
    }

    wbk->Close();

    m_bStopped = true;

    return 0 ;
}

///////////////////////////////////////////////////////////
//
// BOOL WaitWithMessageLoop(HANDLE hEvent)
//
BOOL CUnitThread::WaitWithMessageLoop(HANDLE hEvent)
{
    while (TRUE)
    {
        // Wait for the event and for messages.
        DWORD dwReturn = ::MsgWaitForMultipleObjects(1,
                                                     &hEvent,
                                                     FALSE,
                                                     INFINITE,
                                                     QS_ALLINPUT) ;
        if (dwReturn == WAIT_OBJECT_0)
        {
            // Our event happened.
            ::CloseHandle(hEvent);
            return TRUE ;
        }
        else if (dwReturn == WAIT_OBJECT_0 + 1)
        {
            // Handle message to keep client alive.
            MSG msg ;
            while(::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
            {
                ::TranslateMessage(&msg);
                ::DispatchMessage(&msg) ;
            }
        }
        else
        {
            return FALSE ;
        }
    }
}


 

xlsgen documentation. © ARsT Design all rights reserved.