Lab 2 Operating systems

If you create threads using CreateThread you are using the sort of direct low level call to create the thread. If we have a multithreaded program and we use the normal C runtime librariy we can run into a number of problems in the the C run time support was not designed for a multithreaded environment.  The solution is to create a copy of the static variables used in the C run time library for each thread.  There is a shorthand way of doing this. The solution is to make our threads using calls to _beginthreadex rather that directly to CreateThread. _beginthreadex can be thought of a some code that creates a thread but also arranges to have a copy of the static variables of the C runtime library created for the private use of that thread.

Specification for _beginthread, _beginthreadex

unsigned long _beginthread( void( __cdecl *start_address )( void * ), unsigned stack_size, void *arglist );

unsigned long _beginthreadex( void *security, unsigned stack_size, unsigned ( __stdcall *start_address )( void * ), void *arglist, unsigned initflag, unsigned *thrdaddr );

Routine

Required Header

Compatibility

_beginthread

<process.h>

Win 95, Win NT 

_beginthreadex

<process.h>

Win 95, Win NT 

 

For additional compatibility information, see Compatibility in the Introduction.

Libraries

LIBCMT.LIB

Multithread static library, retail version

MSVCRT.LIB

Import library for MSVCRT.DLL, retail version

 

To use _beginthread or _beginthreadex, the application must link with one of the multithreaded C run-time libraries.

Return Value

If successful, each of these functions returns a handle to the newly created thread. _beginthread returns –1 on an error, in which case errno is set to EAGAIN if there are too many threads, or to EINVAL if the argument is invalid or the stack size is incorrect. _beginthreadex returns 0 on an error, in which case errno and doserrno are set.

Parameters

start_address

Start address of routine that begins execution of new thread

stack_size

Stack size for new thread or 0

arglist

Argument list to be passed to new thread or NULL

security

Security descriptor for new thread; must be NULL for Windows 95 applications

initflag

Initial state of new thread (0 for running or CREATE_SUSPEND for suspended)

thrdaddr

Points to a 32-bit variable that receives the thread identifier

Remarks

The _beginthread function creates a thread that begins execution of a routine at start_address. The routine at start_address must use the __cdecl calling convention and should have no return value. When the thread returns from that routine, it is terminated automatically.

_beginthreadex resembles the Win32 CreateThread API more closely than does _beginthread. _beginthreadex differs from _beginthread in the following ways:


// with Createthread

#include <iostream.h>

#include <stdlib.h>

#include <windows.h>

 

void ThreadFunc(DWORD n)

{

    int i,j;

    for (i=0;i<10;i++)

            {  

        for (j=0;j<30;j++)

                        {

                   cout << n ;

           // Sleep(10*n);

         }

         cout << endl ;}

}

void main(void)

{

    HANDLE hThrds[5];

    DWORD threadId;

    int i;

    //Create 5 threads reusing

    for (i=0; i<5; i++)

    {

        hThrds[i] = CreateThread(0,0,

            (LPTHREAD_START_ROUTINE) ThreadFunc,

            (LPVOID)i,

            0,

            &threadId );

        if (hThrds[i])

        {

            cout << "Thread launched" <<  i<< endl;

           

        }

    }

    // Wait for the threads to complete by putting base thread to sleep.

    // We'll see a better way of doing this later.

     WaitForMultipleObjects(5, hThrds,

    TRUE, INFINITE);

   for (i=0; i<5; i++)

    {

     CloseHandle(hThrds[i]);

   }

}

 

// With _beginthread

 

#include <iostream.h>

#include <stdlib.h>

#include <windows.h>

#include <process.h>

 

void ThreadFunc(LPVOID n)

{

    int i,j;

    for (i=0;i<10;i++)

            {  

        for (j=0;j<30;j++)

                        {

                   cout << (int)n ;

           Sleep(10*(int)n);

         }

         cout << endl ;}

 

}

int main()

{

    HANDLE hThrds[5];

    int i,j;

    //Create 5 threads reusing

    for (i=0; i<5; i++)

    {

        hThrds[i] = (HANDLE) _beginthread(

          ThreadFunc, 0,

            (LPVOID) i

           );

        if (hThrds[i])

        {

            cout << "Thread launched" <<  i<< endl;

           

        }

        else

                        { cout << "Failed to launch Thread" <<  i<< endl;

                           for (j=0; j<i; i++)

                           {CloseHandle(hThrds[j]);}

                        exit(0);}

    }

 

     WaitForMultipleObjects(5, hThrds, TRUE, INFINITE);

   for (i=0; i<5; i++)

    {

     CloseHandle(hThrds[i]);

   }

   return(0);

}


//with _beginthreadex

#include <iostream.h>

#include <stdlib.h>

#include <windows.h>

#include <process.h>

 

DWORD WINAPI ThreadFunc(LPVOID n)

{

    int i,j;

    for (i=0;i<10;i++) 

      {   for (j=0;j<30;j++)

                        {

                   cout << (int)n ;

                  Sleep(10*(int)n);

         }

         cout << endl ;}

            return(0);

}

int main()

{

    HANDLE hThrds[5];

    DWORD aThreadID;

    int i,j;

    //Create 5 threads reusing

    for (i=0; i<5; i++)

    {

        hThrds[i] = (HANDLE) _beginthreadex(

                                    0,0,

            (unsigned (_stdcall *)(void *)) ThreadFunc,

            (LPVOID) i,0,  (unsigned *) &aThreadID

           );

        if (hThrds[i])

        {

            cout << "Thread launched" <<  i<< endl;

        }

            else

                        { cout << "Failed to launch Thread" <<  i<< endl;

                           for (j=0; j<i; i++) CloseHandle(hThrds[j]);

                        exit(0);}

    }

    WaitForMultipleObjects(5, hThrds, TRUE, INFINITE);

   for (i=0; i<5; i++) CloseHandle(hThrds[i]);

   return(0);

}

#include <stdlib.h>

#include <windows.h>

 

void ThreadFunc(DWORD n)

{

    int i,j;

    for (i=0;i<10;i++)

            {  

        for (j=0;j<30;j++)

                        {

                   cout << n ;

           // Sleep(10*n);

         }

         cout << endl ;}

}

void main(void)

{

    HANDLE hThrds[5];

    DWORD threadId;

    int i;

    //Create 5 threads reusing

    for (i=0; i<5; i++)

    {

        hThrds[i] = CreateThread(0,0,

            (LPTHREAD_START_ROUTINE) ThreadFunc,

            (LPVOID)i,

            0,

            &threadId );

        if (hThrds[i])

        {

            cout << "Thread launched" <<  i<< endl;

           

        }

    }

    // Wait for the threads to complete by putting base thread to sleep.

    // We'll see a better way of doing this later.

     WaitForMultipleObjects(5, hThrds,

    TRUE, INFINITE);

   for (i=0; i<5; i++)

    {

     CloseHandle(hThrds[i]);

   }

}


Lab

A.

1. Create a new WIN32 console application (with a new workspace) in Visual studio.

 

2. Check that the project settings are correct
Project->Settings->C/C++ -> Code Generations ->Debug Multithreaded
 
3. Add a new C++ source file for and copy the program using  _beginthread into it
   File->new->Files-> C++ source
 
4. Build it and execute
 
5. Edit source file and remove comments from Sleep statement in Threadfunc
then build it and execute it again.
 
Question
 
Explain the difference behavior between the first execution and the second.
Why is there a run of 0’s at the beginning and a run of 4’s at the end during the second run?
 
B.
1. Replace the thread function in the program using  _beginthread with
 
void ThreadFunc(LPVOID n)
{
    int i,j;
    int h=*((int*)n);
 
    for (i=0;i<10;i++)
               {   for (j=0;j<30;j++)
                  {
                      cout << h ;
                      Sleep(10*h);
                     }
         cout << endl ;}
)
2. In main procedure
change the _beginthread call to
hThrds[i] = (HANDLE) _beginthread(
          ThreadFunc, 0,
            &i
           );
 

What happens when this is executed vs. when the original is run. Why does it happen?

Homework 1

Write 3 programs

  1. Write a program that calls the following function ComputePi () directly as a function.
  2. Write a program that creates a thread to run ComputePi (). To avoid having the base thread terminate before the child use a busy loop that keeps checking if the child thread is finished every so many milliseconds.
  3. Write a program puts the base thread to “sleep” but wakes it up when the thread has completed using WaitForSingleObject().

Each program should calculate and print out the time required to executes

Discuss any diffences in the times among the executions.

Thread function
(requires #include <time.h>) routine that computes the value of PI

 

DWORD WINAPI ComputePi(LPVOID n)

{

    int i;

    int inside = 0;

    double val;

 

    UNREFERENCED_PARAMETER(n);

    /* Seed the random-number generator */

    srand( (unsigned)time( NULL ) );

 

    for (i=0; i<1000000; i++)

    {

        double x = (double)(rand())/RAND_MAX;

        double y = (double)(rand())/RAND_MAX;

        if ( (x*x + y*y) <= 1.0 )

            inside++;

    }

    val = (double)inside / i;

    printf("PI = %.4g\n", val*4);

    return 0;

}

 

Suggestions

 1.Make sure you have the following include files

#include <stream.h>

#include <stdlib.h>

#include <windows.h>

#include <time.h>

 

2. Check that the project settings are correct
Project->Settings->C/C++ -> Code Generations ->Debug Multithreaded
 

*DWORD is a 32 bit unsigned integer
 
Useful Functions
 
1. BOOL GetExitCodeThread(
  HANDLE hThread,      // handle to the thread ( value returned by CreateThread)
  LPDWORD lpExitCode   // address to receive termination status
   );
 

function retrieves the termination status of the specified thread. If it is still running it will return the code STILL_ACTIVE in the second  parameter (here named lpExitCode).

 

 

2. DWORD GetTickCount(  VOID)

The GetTickCount function retrieves the number of milliseconds that have elapsed since the system was started. It is limited to the resolution of the system timer.

This function has no parameters. The return value is the number of milliseconds that have elapsed since the system was started.

3.DWORD WaitForSingleObject(
   HANDLE
hHandle,        // handle to object to wait for
   DWORD dwMilliseconds   // time-out interval in milliseconds
);

The WaitForSingleObject function returns when one of the following occurs:

a. The specified object is in the signaled state.
b. The time-out interval elapses.
 

 
Parameters

HHandle Handle to the object. For a list of the object types whose handles can be specified, see the following Remarks section.

 

dwMilliseconds

Specifies the time-out interval, in milliseconds. The function returns if the interval elapses, even if the object's state is nonsignaled. If dwMilliseconds is zero, the function tests the object's state and returns immediately. If dwMilliseconds is INFINITE, the function's time-out interval never elapses.

Return Values

If the function succeeds, the return value indicates the event that caused the function to return. This value can be one of the following.
 WAIT_TIMEOUT The time-out interval elapsed, and the object's state is nonsignaled.
WAIT_OBJECT_0 The state of the specified object is signaled