Thread: Multi-Threading and ADO

  1. #1
    Registered User
    Join Date
    Apr 2004
    Posts
    10

    Multi-Threading and ADO

    Hi,

    I am creating multiple threads to access a database and retrieve records using ADO. I am getting runtime error "abnormal program termination'.

    My main() function creates an Database connection and recordset object. it also creates multiple thread which accesses ThreadFunc() . it passes the recordset object to ThreadFunc().

    ThreadFunc() function receives the recordset object prints the record and moves the pointer to the next record.

    I am attaching the code below. please correct me.

    Code:
    #import "C:\Program files\Common Files\System\Ado\msado15.dll" no_namespace rename("EOF", "ADOEOF")
    
    #include <windows.h>
    #include <stdio.h>
    #include <ole2.h>
    #include <conio.h>
    
    #define NUM_THREADS 12
    
    CRITICAL_SECTION cs; 
    HRESULT hr;
    _ConnectionPtr pConn;
    _RecordsetPtr pRs;
    
    /*
        ThreadFunc is responsible for accessing the Recordset. and moving through
    	recordset.
    */
    DWORD WINAPI ThreadFunc(LPVOID lpParam) 
    {
          EnterCriticalSection(&cs);
    	  _bstr_t val = pRs->Fields->Item[_variant_t("BTN")]->Value;
    	  printf("%s\n",(char*)val);    
    	  pRs->MoveNext();
          //printf("Hello World, I'm thread# %d\n", (int)lpParam);
    	  //printf("hello world \n");
          LeaveCriticalSection(&cs);
          return 0;
    }//ThreadFunc
    
    void main(){
    
    	DWORD dwThreadId;
        HANDLE hThread[NUM_THREADS];
        int n;    
    	CoInitialize(NULL);
    	InitializeCriticalSection(&cs);
        // Create an ADO connection to database
    	try{
    		hr = pConn.CreateInstance(__uuidof(Connection));
    		hr = pRs.CreateInstance(__uuidof(Recordset)); 
    		_bstr_t strConn("Provider=sqloledb;server=SINF005;Trusted_Connection=yes;database=Core;");
    		pConn->Open(strConn,"","",adConnectUnspecified);
    		pRs->Open("SELECT top 10 Npa_Num from Npa (NOLOCK)", pConn.GetInterfacePtr(), adOpenForwardOnly, adLockReadOnly, adCmdText);
    		//pRs->Open("SELECT BTN from mbs_GetSpitFireEasyPay (NOLOCK)", pConn.GetInterfacePtr(), adOpenForwardOnly, adLockReadOnly, adCmdText);
    	}catch(_com_error &e){
    		printf("Error\n");
    		printf("\tCode meaning = %s", e.ErrorMessage());
    	} 
    
    	// create all the threads
        for (n = 0; n < NUM_THREADS; n++)
        {
            hThread[n] = CreateThread(NULL, 0, ThreadFunc, &pRs, 0, &dwThreadId);  
            if (hThread == NULL)
                fprintf(stderr, "Failed to create thread# %d", n);
        }//for
    	for (n = 0; n < NUM_THREADS; n++)
        {
            if (hThread[n] != NULL)
                WaitForSingleObject(hThread[n], INFINITE);
        }//for
    
        DeleteCriticalSection(&cs);
    	printf("This is after all threads exited\n");
    	CoUninitialize();
    }
    Thanks

  2. #2
    &TH of undefined behavior Fordy's Avatar
    Join Date
    Aug 2001
    Posts
    5,793
    Cant load your code as I dont have the database to test it...

    But have you tried calling CoInitialize in the thread func? That API allows a thread to use COM and as you are creating new threads, I guess that should get a call too...

  3. #3
    Registered User
    Join Date
    Apr 2004
    Posts
    10
    I used CoInitialize(NULL) and CoUnitialize(NULL) as the first and last statement in ThreadFunc(), still I am getting errors.

  4. #4
    &TH of undefined behavior Fordy's Avatar
    Join Date
    Aug 2001
    Posts
    5,793
    If you follow the code through a debugger, you will see that the global recordset is null off when accessed by another thread...

    There is way in COM to share interfaces via a Global Interface Table...

    Do the following;

    After creating your recordset (before creating the threads) Use this code to register your interface as shared;

    Code:
    IGlobalInterfaceTable *lpGIT;
    	assert(SUCCEEDED(CoCreateInstance(
    CLSID_StdGlobalInterfaceTable, 
    		NULL,CLSCTX_INPROC_SERVER,IID_IGlobalInterfaceTable, (void **)&lpGIT))); 
    
    	
    	lpGIT->RegisterInterfaceInGlobal(pRs,__uuidof(_Recordset),&dwCookie);
    You dont have to use the assert...but whatever

    Now in the ThreadFunc (after entering the Critical Section) do this

    Code:
    _Recordset *lpRec = 0;
    	IGlobalInterfaceTable *lpGIT;
    	CoCreateInstance(CLSID_StdGlobalInterfaceTable, NULL,CLSCTX_INPROC_SERVER,
    		IID_IGlobalInterfaceTable, (void **)&lpGIT); 
    	
    	lpGIT->GetInterfaceFromGlobal(dwCookie,__uuidof(_Recordset),(void**)&lpRec);
    
    
    	_bstr_t val = lpRec->Fields->GetItem(_variant_t("somevalue"))->Value;
    	printf("%s\n",(char*)val);    
    	lpRec->MoveNext();
    	lpGIT->Release();
    This should now work.

    [edit] took out some AddRef calls that didnt belong

    Oh..and dont forget the final lpGIT->Release(); at the end of main.

  5. #5
    Yes, my avatar is stolen anonytmouse's Avatar
    Join Date
    Dec 2002
    Posts
    2,544
    EDIT: Beaten!

    Passing COM interface pointers between threads is a somewhat complicated affair. This page on multi-threading and COM, although Delphi centric, provides a good overview. This codeguru article on COM threading may also be helpful. You can get more information via a search on COM threading.

    Basically, when you wish to pass an interface pointer between threads you must marshal it. There are a couple of "simple" methods to do this. The first is the briefly named CoMarshalInterThreadInterfaceInStream()/CoGetInterfaceAndReleaseStream() functions. There are also ATL wrappers for these functions.
    Code:
    /*
        ThreadFunc is responsible for accessing the Recordset. and moving through
    	recordset.
    */
    DWORD WINAPI ThreadFunc(LPVOID lpParam) 
    {
          EnterCriticalSection(&cs);
    
              // Don't make pRs global
              _RecordsetPtr pRs;
    
              // Unmarshal interface from pStream
              LPSTREAM pStream = (LPSTREAM) lpParam;
              CoGetInterfaceAndReleaseStream(pStream, __uuidof(Recordset), (void **) &pRs);
    
              // Now you can safely use pRs in this thread.
    
    
    	  _bstr_t val = pRs->Fields->Item[_variant_t("BTN")]->Value;
    	  printf("%s\n",(char*)val);    
    	  pRs->MoveNext();
          //printf("Hello World, I'm thread# %d\n", (int)lpParam);
    	  //printf("hello world \n");
          LeaveCriticalSection(&cs);
          return 0;
    }//ThreadFunc
    
    void main(){
    
    	DWORD dwThreadId;
        HANDLE hThread[NUM_THREADS];
        int n;    
    	CoInitialize(NULL);
    	InitializeCriticalSection(&cs);
        // Create an ADO connection to database
    	try{
    		hr = pConn.CreateInstance(__uuidof(Connection));
    		hr = pRs.CreateInstance(__uuidof(Recordset)); 
    		_bstr_t strConn("Provider=sqloledb;server=SINF005;Trusted_Connectio  n=yes;database=Core;");
    		pConn->Open(strConn,"","",adConnectUnspecified);
    		pRs->Open("SELECT top 10 Npa_Num from Npa (NOLOCK)", pConn.GetInterfacePtr(), adOpenForwardOnly, adLockReadOnly, adCmdText);
    		//pRs->Open("SELECT BTN from mbs_GetSpitFireEasyPay (NOLOCK)", pConn.GetInterfacePtr(), adOpenForwardOnly, adLockReadOnly, adCmdText);
    	}catch(_com_error &e){
    		printf("Error\n");
    		printf("\tCode meaning = %s", e.ErrorMessage());
    	} 
    
    	// create all the threads
        for (n = 0; n < NUM_THREADS; n++)
        {
            // Marshal interface into pStream
            LPSTREAM pStream;
            CoMarshalInterThreadInterfaceInStream(__uuidof(Recordset), pRs, &pStream);
    
            hThread[n] = CreateThread(NULL, 0, ThreadFunc, pStream, 0, &dwThreadId);
            if (hThread == NULL)
                fprintf(stderr, "Failed to create thread# %d", n);
        }//for
    	for (n = 0; n < NUM_THREADS; n++)
        {
            if (hThread[n] != NULL)
                WaitForSingleObject(hThread[n], INFINITE);
        }//for
    
        DeleteCriticalSection(&cs);
    	printf("This is after all threads exited\n");
    	CoUninitialize();
    }
    The other method is to use a global interface table in the form of IGlobalInterfaceTable. You add an interface using RegisterInterfaceInGlobal() and can retrieve the interface from other threads using GetInterfaceFromGlobal(). This method may be simpler, especially when you have multiple interface pointers to marshal:
    Code:
    #import "C:\Program files\Common Files\System\Ado\msado15.dll" no_namespace rename("EOF", "ADOEOF")
    
    #include <windows.h>
    #include <stdio.h>
    #include <ole2.h>
    #include <conio.h>
    
    #define NUM_THREADS 12
    
    CRITICAL_SECTION cs; 
    HRESULT hr;
    DWORD g_dwConnCookie;
    DWORD g_dwRsCookie;
    IGlobalInterfaceTable * g_pGIT;
    
    DWORD WINAPI ThreadFunc(LPVOID lpParam) 
    {
    	EnterCriticalSection(&cs);
    
    		// Retrieve desired interface pointers from global interface table
    		_RecordsetPtr pRs;
    		g_pGIT->GetInterfaceFromGlobal(g_dwRsCookie, __uuidof(Recordset), (void **) &pRs);
    
    		_bstr_t val = pRs->Fields->Item[_variant_t("BTN")]->Value;
    		printf("%s\n",(char*)val);    
    		pRs->MoveNext();
    	LeaveCriticalSection(&cs);
    
    	return 0;
    }
    
    void main(){
    
    	DWORD dwThreadId;
    	HANDLE hThread[NUM_THREADS];
    	int n;    
    	_ConnectionPtr pConn;
    	_RecordsetPtr pRs;
    
    	CoInitialize(NULL);
    	InitializeCriticalSection(&cs);
    
    	// Create global interface table
    	CoCreateInstance(CLSID_StdGlobalInterfaceTable,
    	                 NULL,
                             CLSCTX_INPROC_SERVER,
                             IID_IGlobalInterfaceTable,
                             (void **) &g_pGIT);
    
    	try
    	{
    		hr = pConn.CreateInstance(__uuidof(Connection));
    		hr = pRs.CreateInstance(__uuidof(Recordset)); 
    		_bstr_t strConn("Provider=sqloledb;server=SINF005;Trusted_Connectio  n=yes;database=Core;");
    		pConn->Open(strConn,"","",adConnectUnspecified);
    		pRs->Open("SELECT top 10 Npa_Num from Npa (NOLOCK)", pConn.GetInterfacePtr(), adOpenForwardOnly, adLockReadOnly, adCmdText);
    		//pRs->Open("SELECT BTN from mbs_GetSpitFireEasyPay (NOLOCK)", pConn.GetInterfacePtr(), adOpenForwardOnly, adLockReadOnly, adCmdText);
    	}
    	catch(_com_error &e)
    	{
    		printf("Error\n");
    		printf("\tCode meaning = %s", e.ErrorMessage());
    	} 
    
    	// Add interface pointers to global interface table
    	g_pGIT->RegisterInterfaceInGlobal((IUnknown *) pRs,   __uuidof(Recordset),  &g_dwRsCookie);
    	g_pGIT->RegisterInterfaceInGlobal((IUnknown *) pConn, __uuidof(Connection), &g_dwConnCookie);
    
    	// create all the threads
    	for (n = 0; n < NUM_THREADS; n++)
    	{
    		hThread[n] = CreateThread(NULL, 0, ThreadFunc, &pRs, 0, &dwThreadId);  
    
    		if (hThread[n] == NULL)
    			fprintf(stderr, "Failed to create thread# %d", n);
    	}
    
    	for (n = 0; n < NUM_THREADS; n++)
    	{
    		if (hThread[n] != NULL)
    			WaitForSingleObject(hThread[n], INFINITE);
    	}
    
    	DeleteCriticalSection(&cs);
    	printf("This is after all threads exited\n");
    	CoUninitialize();
    }
    MSDN has an article, In-Process Marshaling Helpers, that describes both these methods.

    Neither of these are compiled examples so you will have to fix the variables, release everything, etc.
    Last edited by anonytmouse; 05-05-2004 at 08:49 PM.

  6. #6
    Registered User
    Join Date
    Apr 2004
    Posts
    10
    I am sorry to be ignorant. I used both registering the interface and used streams to send my recordset object. It is compiling properly but I am getting the same runtime error "abnormal Program termination". Please Help!!!

  7. #7
    &TH of undefined behavior Fordy's Avatar
    Join Date
    Aug 2001
    Posts
    5,793
    Quote Originally Posted by chandhru
    I am sorry to be ignorant. I used both registering the interface and used streams to send my recordset object. It is compiling properly but I am getting the same runtime error "abnormal Program termination". Please Help!!!
    Well both should work (the Interface Table method definately as I ammended your code using tht and it worked)

    Also, I would put the code between entering and leaving the critical section inside a try catch statement. It's probably a _com_error being thrown and not caught that's crashing your prog...this could happen with null interfaces (problem when sharing interface pointers between threads) but it could also happen if you are going past the EOF mark on the recordset (IE if the number of threads exceeds the number of records returned in the recordset)

    Catch the exception and look at the ErrorMessage

  8. #8
    Yes, my avatar is stolen anonytmouse's Avatar
    Join Date
    Dec 2002
    Posts
    2,544
    >> but it could also happen if you are going past the EOF mark on the recordset (IE if the number of threads exceeds the number of records returned in the recordset)<<

    Specifically, you are returning 10 records ("SELECT top 10") and calling MoveNext() 12 times. You can test the recordset using the Recordset::EOF() method.

  9. #9
    Registered User
    Join Date
    Jan 2003
    Posts
    648
    Code:
    void main(){
    Sorry for being picky but oh no... just change the void to int. You don't even have to return anything, the standard will automatically put in a return statement (although its recommended).

  10. #10
    Registered User
    Join Date
    Apr 2004
    Posts
    10
    First of all I would like to Thank Fordy and anonytmouse for their great help. after inserting the modifications you mentioned in my code it finally worked.

    Now I am facing a different problem. for example if my database has 100 records and I create only 10 threads. the present program exits after retrieving 10 records and the remaining 90 records are not retrieved.

    That is, My Multi-threading concept after executing the ThreadFunc() only once gets exited. Is there an way that my Multi-threading does not exit till my Recordset EOF is not reached. i.e. till my Recordset is not empty that it will continue to execute ThreadFunc()

    Also Is there an possiblity that I can distinguish the threads.

    I am attaching the modified code here.

    Code:
    #import "C:\Program files\Common Files\System\Ado\msado15.dll" no_namespace rename("EOF", "ADOEOF")
    #include <ole2.h>
    #include <stdio.h>
    #include <windows.h>
    #define NUM_THREADS 10
    
    CRITICAL_SECTION cs; 
    HRESULT hr;
    DWORD dwRsCookie;
    IGlobalInterfaceTable * g_pGIT;
    
    
    /*
        ThreadFunc is responsible for accessing the Recordset. and moving through
    	recordset.
    */
    DWORD WINAPI ThreadFunc(LPVOID lpParam){
    	try{
    		EnterCriticalSection(&cs);
    		CoInitialize(NULL);
    		_RecordsetPtr pRs;
    
    		IGlobalInterfaceTable *lpGIT;
    		CoCreateInstance(CLSID_StdGlobalInterfaceTable, NULL,CLSCTX_INPROC_SERVER,
    		IID_IGlobalInterfaceTable, (void **)&lpGIT); 
    		lpGIT->GetInterfaceFromGlobal(dwRsCookie,__uuidof(_Recordset),(void**)&pRs);
    
    		if(pRs->ADOEOF){
    			printf("The Recordset Object is empty \n");
    			exit(0);
    		}
    		_bstr_t val = pRs->Fields->GetItem(_variant_t("Npa_num"))->Value;
    		printf("%s \n",(char*)val);
    		pRs->MoveNext();
    		
    		lpGIT->Release();
    		CoUninitialize();
    		LeaveCriticalSection(&cs);
    	}catch(_com_error &e){
    		printf("\tCode = %08lx\n", e.Error());
    		printf("\tCode meaning = %s\n", e.ErrorMessage());
    		return 0;
    	}
    	return 0;
    }
    
    int main(){
    	DWORD dwThreadId;
        HANDLE hThread[NUM_THREADS];
        int n;    
    
    	CoInitialize(NULL);
    	_ConnectionPtr pConn;
    	_RecordsetPtr pRs;
    
    	try{
    		hr = pConn.CreateInstance(__uuidof(Connection));
    		hr = pRs.CreateInstance(__uuidof(Recordset));
            _bstr_t strConn("Provider=sqloledb;server=ALBHM01WSINF005;Trusted_Connection=yes;database=Core;");
            pConn->Open(strConn,"","",adConnectUnspecified);
    		pRs->Open("SELECT top 10 Npa_Num from Npa (NOLOCK)", pConn.GetInterfacePtr(), adOpenForwardOnly, adLockReadOnly, adCmdText);
    		//pRs->Open("SELECT * from Npa (NOLOCK)", pConn.GetInterfacePtr(), adOpenForwardOnly, adLockReadOnly, adCmdText);
    
    	}catch(_com_error &e){
    		printf("Error\n");
    		printf("\tCode meaning = %s", e.ErrorMessage());
    	}
    	CoUninitialize();
    	IGlobalInterfaceTable *lpGIT;
    	CoCreateInstance(
    		CLSID_StdGlobalInterfaceTable, 
    		NULL,CLSCTX_INPROC_SERVER,
    		IID_IGlobalInterfaceTable,
    		(void **)&lpGIT); 
    	lpGIT->RegisterInterfaceInGlobal(pRs,__uuidof(_Recordset)  ,&dwRsCookie);
    	InitializeCriticalSection(&cs);
    
    	// create all the threads 
        for (n = 0; n < NUM_THREADS; n++){
            hThread[n] = CreateThread(NULL, 0, ThreadFunc, &pRs, 0, &dwThreadId);  
            if (hThread == NULL)
                fprintf(stderr, "Failed to create thread# %d", n);
        }//for
    	for (n = 0; n < NUM_THREADS; n++)
        {
            if (hThread[n] != NULL)
                WaitForSingleObject(hThread[n], INFINITE);
        }//for
    	lpGIT->Release();
        DeleteCriticalSection(&cs);
    	printf("This is after all threads exited\n"); 
    	return 0;
    }

  11. #11
    &TH of undefined behavior Fordy's Avatar
    Join Date
    Aug 2001
    Posts
    5,793
    Currently you are setting the number of threads in your for loop to 10 (NUM_THREADS)....why not se it to Recordset::RecordCount...that way each member of the recordset ill get a thread to access it.

    >>Also Is there an possiblity that I can distinguish the threads.

    Dont quite understand the meaning here....you can differentiate a thread by passing a unique param to the lpParameter argument of CreateThread...or you can store thread specific information using Thread Local Storage (Lookup TlsAlloc, TlsGetValue, TlsSetValue, TlsFree)

  12. #12
    Registered User
    Join Date
    Apr 2004
    Posts
    10
    The problem I have is I am limited on the number of threads I need to use. I have an Intel Dialogic card with 48 lines (2 T1).

    So my concept is each line is going to be a single Thread and they are going to get the records (which has telphone numbers) and make calls on those lines.

    so that is the main reason, I cannot use "Recordset::RecordCount".

    also by distinguishing threads I meant if there were 500 records and I wanted to know which line (thread) got which telephone number.

    I am already passing Recordset object (&pRs) in my lpParameter argument, so how can I pass a unique number in to it.

    Code:
            hThread[n] = CreateThread(NULL, 0, ThreadFunc, &pRs, 0, &dwThreadId);

  13. #13
    Yes, my avatar is stolen anonytmouse's Avatar
    Join Date
    Dec 2002
    Posts
    2,544
    I'd consider doing all the database operations in the main thread and passing any information that is needed to the worker threads. This will provide a much simpler, more robust and higher performance solution. If you need to pass more than one value to the worker threads you can do so in a structure.

    As for limiting the number of worker threads running at any one time you can use a semaphore. A semaphore keeps a count. Every time you wait on the semaphore the count is reduced by one. If the count is zero, the wait will return only when the count becomes greater than zero. You increment the count by calling ReleaseSemaphore(). So, to implement a maximum thread feature, you wait for the semaphore before creating a thread and release the semaphore when exiting a thread.

    Here is how I would do it:
    Code:
    #import "C:\Program files\Common Files\System\Ado\msado15.dll" no_namespace rename("EOF", "ADOEOF")
    #include <ole2.h>
    #include <stdio.h>
    #include <windows.h>
    #define MAX_THREADS 48
    
    HANDLE g_hSemaphore;
    CRITICAL_SECTION cs; 
    HRESULT hr;
    
    DWORD WINAPI ThreadFunc(LPVOID lpParam)
    {
    	EnterCriticalSection(&cs);
    	_bstr_t val((BSTR) lpParam, false);
    	printf("%s \n",(char*) val);
    	LeaveCriticalSection(&cs);
    
    	// Increase the semaphore count - allowing another thread to be launched.
    	ReleaseSemaphore(g_hSemaphore, 1, NULL);
    	return 0;
    }
    
    int main(void)
    {
    	DWORD dwThreadId;
    	HANDLE hThread;
    	_ConnectionPtr pConn;
    	_RecordsetPtr pRs;
    
    	CoInitialize(NULL);
    	InitializeCriticalSection(&cs);
    	g_hSemaphore = CreateSemaphore(NULL, MAX_THREADS, MAX_THREADS, NULL);
    
    	try
    	{
    		hr = pConn.CreateInstance(__uuidof(Connection));
    		hr = pRs.CreateInstance(__uuidof(Recordset));
    		_bstr_t strConn("Provider=sqloledb;server=ALBHM01WSINF005;Trusted_C  onnection=yes;database=Core;");
    		pConn->Open(strConn,"","",adConnectUnspecified);
    		pRs->Open("SELECT top 10 Npa_Num from Npa (NOLOCK)", pConn.GetInterfacePtr(), adOpenForwardOnly, adLockReadOnly, adCmdText);
    		//pRs->Open("SELECT * from Npa (NOLOCK)", pConn.GetInterfacePtr(), adOpenForwardOnly, adLockReadOnly, adCmdText);
    
    		while (!pRs->ADOEOF) // Create all the threads - Loop until we reach the EOF.
    		{
    			// Get value from database.
    			_bstr_t val = pRs->Fields->GetItem(_variant_t("Npa_num"))->Value;
    			pRs->MoveNext();
    
    			// Wait until the semaphore count is greater than zero
    			// This means a thread is available. This also decrements the semaphore count.
    			WaitForSingleObject(g_hSemaphore, INFINITE);
    
    			// Pass BSTR value to new thread. If you need to pass more parameters use a structure.
    			hThread = CreateThread(NULL, 0, ThreadFunc, val.Detach(), 0, &dwThreadId);
    
    			if (hThread == NULL)
    				fprintf(stderr, "Failed to create thread");
    			else
    				CloseHandle(hThread);
    		}
    	}
    	catch(_com_error &e)
    	{
    		printf("Error\n");
    		printf("\tCode meaning = %s", e.ErrorMessage());
    	}
    
    	// Wait for all our threads to close by consuming all of the semaphore count.
    	for (int n = 0;n < MAX_THREADS;n++)
    	{
    		WaitForSingleObject(g_hSemaphore, INFINITE);
    	}
    
    	printf("This is after all threads exited\n"); 
    
    	DeleteCriticalSection(&cs);
    	CloseHandle(g_hSemaphore);
    	CoUninitialize();
    	return 0;
    }
    P.S If you don't intermix spaces and tabs, your code will be correctly spaced when you post it here.

    EDIT:
    >> also by distinguishing threads I meant if there were 500 records and I wanted to know which line (thread) got which telephone number. <<

    You could do this with an stl container. Possibly a map.
    Last edited by anonytmouse; 05-09-2004 at 01:04 AM.

  14. #14
    Registered User
    Join Date
    Apr 2004
    Posts
    10
    I went through semaphores. I still have some doubts. The main reason I am going for multi-threading is I wanted all the 48 telephone lines to call simultaneously.

    I represent each telephone line to a single thread, but with my concept of Enter critical section and leave critical section. will my threads will call simultaneously or will wait for one thread to call and the other will resume after the first thread executes.

    Code:
    DWORD WINAPI ThreadFunc(LPVOID lpParam)
    {
    	EnterCriticalSection(&cs);
    	_bstr_t val((BSTR) lpParam, false);
    	printf("%s \n",(char*) val);
    	LeaveCriticalSection(&cs);
    
    	// Increase the semaphore count - allowing another thread to be launched.
    	ReleaseSemaphore(g_hSemaphore, 1, NULL);
    	return 0;
    }
    In the place of printf(), I will call the function to dialogic box which will make telephone call. as having critical sections will it cause the single thread to make call at a time and will other threads wait on for the thread which has control to make calls.

    please correct me.

    Also with semaphores, even though there will be 48 threads at any time. is it possible that the thread that was created first retrieves the tel. number from database and will die after making a call.

    The main reason I am concerned is I wanted to know which tel. line called which number from database.

  15. #15
    Yes, my avatar is stolen anonytmouse's Avatar
    Join Date
    Dec 2002
    Posts
    2,544
    >> as having critical sections will it cause the single thread to make call at a time and will other threads wait on for the thread which has control to make calls. <<

    Yes. You should only protect non-thread safe code with the critical section. Code that can run concurrently in all the threads should not be inside the critical section.

    >> is it possible that the thread that was created first retrieves the tel. number from database and will die after making a call. <<

    Yes, the thread will finish when the ThreadFunc returns.

    >> The main reason I am concerned is I wanted to know which tel. line called which number from database. <<

    It sounds like you need to know when a thread exits so that you can reuse that line number. One way you can do this is with a container of line numbers.
    Code:
    // Pool of line numbers
    bool g_bLinesUsed[48];
    
    // Use an stl map to map telephone numbers to line numbers
    map<_bstr_t, int> g_ph_To_ln;
    
    
    //
    // Find a free line number in the pool
    //
    int FindFreeLine(void)
    {
    	int ret = -1;
    	EnterCriticalSection(&cs);
    
    	for (int i = 0;i < 48;i++)
    	{
    		if (g_bLinesUsed[i] == false)
    		{
    			g_bLinesUsed[i] = true;
    			ret = i;
    		}
    	}
    
    	LeaveCriticalSection(&cs);
    
    	return ret;
    }
    
    //
    // Return a line number to the pool
    //
    void FreeLine(int nLine)
    {
    	EnterCriticalSection(&cs);
    	g_bLinesUsed[nLine] = false;
    	LeaveCriticalSection(&cs);
    }
    
    //
    // Map the phone number to a line number in the stl container
    //
    void MapPhoneToLine(_bstr_t bsPhone, int nLine)
    {
    	EnterCriticalSection(&cs);
    	g_ph_to_ln[bsPhone] = nLine;
    	LeaveCriticalSection(&cs);
    }
    
    //
    // Return the line number associated with a phone number
    //
    int GetLineNumberForPhone(_bstr_t bsPhone)
    {
    	EnterCriticalSection(&cs);
    	int ret = g_ph_to_ln[bsPhone];
    	LeaveCriticalSection(&cs);
    	return ret;
    }
    
    DWORD WINAPI ThreadFunc(LPVOID lpParam)
    {
    	// Get phone number from param
    	_bstr_t val((BSTR) lpParam, false);
    
    	// Get a line number to use
    	int nLine = FindFreeLine();
    
    	// Map phone number to line number.
    	MapPhoneToLine(val, nLine);
    
    	EnterCriticalSection(&cs);
    	printf("%s \n",(char*) val);
    	LeaveCriticalSection(&cs);
    
    	// Return the line number to the pool after we have finished with it.
    	FreeLine(nLine);
    	ReleaseSemaphore(g_hSemaphore, 1, NULL);
    	return 0;
    }
    
    
    // Now you can use elsewhere to retrieve a line number associated with
    // a phone number:
    printf("Line number for 555-5678 is %d", GetLineNumberForPhone("555-5678"));
    Note: I haven't used an stl map before so the code may not be completely correct.
    Last edited by anonytmouse; 05-09-2004 at 10:16 PM. Reason: Replace = with ==

Popular pages Recent additions subscribe to a feed