|< Windows Process & Threads: C Run-Time 3 | Main | Windows Process & Threads: C Run-Time 5 >| Site Index | Download |


 

 

 

 

 

MODULE S

PROCESSES AND THREADS: C RUN-TIME

Part 4: Program Examples

 

 

 

 

 

The Page Index:

  1. Functions & Misc.

  2. _beginthread(), _beginthreadex()

  3. _endthread(), _endthreadex()

  4. Compiler Options for Multithread program

  5. Program Example

  6. Another Example

  7. _doserrno, errno, _sys_errlist, and _sys_nerr

 

 

 

 

 

 

 

 

 

 

 

 

 

 

My Training Period: xx hours. Before you begin, read some instruction here.

 

The expected abilities:

  • Able to understand and use functions from Windows C Run-Time Library in your process and thread programs.

  • Able to use Visual C++ / .Net compiler to build and run your process and thread programs.

  • Able to understand and use various functions related to process and thread in your programs.

_beginthread(), _beginthreadex()

 

Information

Description

The function

 _beginthread(), _beginthreadex()

The use

To create a thread.

The prototype

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

 

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

Example

-

The 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 - Pointer to a SECURITY_ATTRIBUTES structure that determines whether the returned handle can be inherited by child processes. If NULL, the handle cannot be inherited. Must be NULL for Windows 95 applications.

initflag - Initial state of new thread (0 for running or CREATE_SUSPENDED for suspended); use ResumeThread() to execute the thread.

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

The return value

If successful, each of these functions returns a handle to the newly created thread. _beginthread() returns –1L 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.

The header file

_beginthread() - <process.h> and _beginthreadex() - <process.h>

Remarks

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

 

Table 1

 

Some 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 _beginthread() does. _beginthreadex() differs from _beginthread() in the following ways:

  1. _beginthreadex() has three additional parameters: initflag, security, threadaddr. The new thread can be created in a suspended state, with a specified security (Windows NT only), and can be accessed using thrdaddr, which is the thread identifier.

  2. The routine at start_address passed to _beginthreadex() must use the __stdcall calling convention and must return a thread exit code.

  3. _beginthreadex() returns 0 on failure, rather than –1L.

  4. A thread created with _beginthreadex() is terminated by a call to _endthreadex().

 

The _beginthreadex() function gives you more control over how the thread is created than _beginthread() does. The _endthreadex() function is also more flexible. For example, with _beginthreadex(), you can use security information, set the initial state of the thread (running or suspended), and get the thread identifier of the newly created thread. You are also able to use the thread handle returned by _beginthreadex() with the synchronization APIs, which you cannot do with _beginthread(). It is safer to use _beginthreadex() than _beginthread(). If the thread spawned by _beginthread() exits quickly, the handle returned to the caller of _beginthread() may be invalid or, worse, point to another thread. However, the handle returned by _beginthreadex() has to be closed by the caller of _beginthreadex(), so it is guaranteed to be a valid handle if _beginthreadex() did not return an error. You can call _endthread() or _endthreadex() explicitly to terminate a thread; however, _endthread() or _endthreadex() is called automatically when the thread returns from the routine passed as a parameter. Terminating a thread with a call to _endthread() or _endthreadex() helps to ensure proper recovery of resources allocated for the thread. _endthread() automatically closes the thread handle (whereas _endthreadex() does not). Therefore, when using _beginthread() and _endthread(), do not explicitly close the thread handle by calling the Win32 CloseHandle() API. This behavior differs from the Win32 ExitThread() API.

For an executable file linked with LIBCMT.LIB, do not call the Win32 ExitThread() API; this prevents the run-time system from reclaiming allocated resources. _endthread() and _endthreadex() reclaim allocated thread resources and then call ExitThread(). The operating system handles the allocation of the stack when either _beginthread() or _beginthreadex() is called; you do not need to pass the address of the thread stack to either of these functions. In addition, the stack_size argument can be 0, in which case the operating system uses the same value as the stack specified for the main thread. The arglist is a parameter to be passed to the newly created thread. Typically it is the address of a data item, such as a character string. arglist may be NULL if it is not needed, but _beginthread() and _beginthreadex() must be provided with some value to pass to the new thread. All threads are terminated if any thread calls abort(), exit(), _exit(), or ExitProcess().

 

_endthread(), _endthreadex()

 

Information

Description

The function

_endthread(), _endthreadex().

The use

To terminate a thread created by _beginthread() or _beginthreadex(), respectively.

The prototype

void _endthread(void);

void _endthreadex(unsigned retval);

Example

-

The parameters

retval - Thread exit code.

The return value

none

The header file

_endthread() - <process.h> and _endthreadex() - <process.h>

Remarks

-

 

Table 2

 

Remarks

 

You can call _endthread() or _endthreadex() explicitly to terminate a thread; however, _endthread() or _endthreadex() is called automatically when the thread returns from the routine passed as a parameter to _beginthread() or _beginthreadex(). Terminating a thread with a call to _endthread() or _endthreadex() helps to ensure proper recovery of resources allocated for the thread.

Note:   For an executable file linked with LIBCMT.LIB, do not call the Win32 ExitThread() API; this prevents the run-time system from reclaiming allocated resources. _endthread() and _endthreadex() reclaim allocated thread resources and then call ExitThread(). _endthread() automatically closes the thread handle. This behavior differs from the Win32 ExitThread() API. Therefore, when you use _beginthread() and _endthread(), do not explicitly close the thread handle by calling the Win32 CloseHandle() API. Like the Win32 ExitThread() API, _endthreadex() does not close the thread handle. Therefore, when you use _beginthreadex() and _endthreadex(), you must close the thread handle by calling the Win32 CloseHandle() API.

 

Compiler Options for Multithread program

 

For Visual C++: The using of the /MD, /ML, /MT, /LD compiler options (Run-Time Library), the explanation is given in the following Table.

The /MD[d], /ML[d], /MT[d], /LD[d] options select either single-threaded or multithreaded run-time routines, indicate if a multithreaded module is a DLL, and select retail or debug versions of the run-time library.

 

Option

Description

/MD

Defines _MT and _DLL so that both multithread- and DLL-specific versions of the run-time routines are selected from the standard .h files. This option also causes the compiler to place the library name MSVCRT.lib into the .obj file.

Applications compiled with this option are statically linked to MSVCRT.lib. This library provides a layer of code that allows the linker to resolve external references. The actual working code is contained in MSVCR71.DLL, which must be available at run time to applications linked with MSVCRT.lib.

When /MD is used with _STATIC_CPPLIB defined (/D_STATIC_CPPLIB) it will cause the application to link with the static multithread Standard C++ Library (libcpmt.lib) instead of the dynamic version (msvcprt.lib) while still dynamically linking to the main CRT via msvcrt.lib.

/MDd

Defines _DEBUG, _MT, and _DLL so that debug multithread- and DLL-specific versions of the run-time routines are selected from the standard .h files. It also causes the compiler to place the library name MSVCRTD.lib into the .obj file.

/ML

Causes the compiler to place the library name LIBC.lib into the .obj file so that the linker will use LIBC.lib to resolve external symbols. This is the compiler's default action. LIBC.lib does not provide multithread support.

/MLd

Defines _DEBUG and causes the compiler to place the library name LIBCD.lib into the .obj file so that the linker will use LIBCD.lib to resolve external symbols. LIBCD.lib does not provide multithread support.

/MT

Defines _MT so that multithread-specific versions of the run-time routines are selected from the standard header (.h) files. This option also causes the compiler to place the library name LIBCMT.lib into the .obj file so that the linker will use LIBCMT.lib to resolve external symbols. Either /MT or /MD (or their debug equivalents /MTd or /MDd) is required to create multithreaded programs.

/MTd

Defines _DEBUG and _MT. Defining _MT causes multithread-specific versions of the run-time routines to be selected from the standard .h files. This option also causes the compiler to place the library name LIBCMTD.lib into the .obj file so that the linker will use LIBCMTD.lib to resolve external symbols. Either /MTd or /MDd (or their non-debug equivalents /MT or MD) is required to create multithreaded programs.

/LD

Creates a DLL.

Passes the /DLL option to the linker. The linker looks for, but does not require, a DllMain() function. If you do not write a DllMain() function, the linker inserts a DllMain() function that returns TRUE.

Links the DLL startup code.

Creates an import library (.lib), if an export (.exp) file is not specified on the command line; you link the import library to applications that call your DLL.

Interprets /Fe as naming a DLL rather than an .exe file; the default program name becomes basename.dll instead of basename.exe.

Changes the default run-time library support to /MT if you have not explicitly specified one of the /M options.

/LDd

Creates a debug DLL. Defines _DEBUG.

 

Table 3

 

Caution:   Do not mix static and dynamic versions of the run-time libraries. Having more than one copy of the run-time libraries in a process can cause problems, because static data in one copy is not shared with the other copy. The linker prevents you from linking with both static and dynamic versions within one .exe file, but you can still end up with two (or more) copies of the run-time libraries. For example, a dynamic-link library linked with the static (non-DLL) versions of the run-time libraries can cause problems when used with an .exe file that was linked with the dynamic (DLL) version of the run-time libraries. You should also avoid mixing the debug and non-debug versions of the libraries in one process. To set this compiler option in the Visual Studio development environment for Visual C++.

 

  1. Open the project's Property Pages dialog box.
  2. Click the C/C++ folder/tab.
  3. Click the Code Generation property page.
  4. Modify the Runtime Library property.

 

Setting Visual C++ for multithreaded programs

 

For Visual Studio .Net (C++ .Net - shown in the following figures):

Project menu (or other short-cut keys) your_projet_name Properties… C/C++ folder Code Generation.

 

Setting Visual C++/.Net for multithreaded programs

 

Setting Visual C++/.Net for multithreaded program

 

A Program Example

 

The following example uses _beginthread() and _endthread().

 

// mythread.cpp

// compile with: /MT /D "_X86_" /c for Visual C++/.Net

#include <windows.h>

/* _beginthread(), _endthread() */

#include <process.h>

#include <stddef.h>

#include <stdio.h>

#include <stdlib.h>

#include <conio.h>

 

/* Function prototypes... */

void Bounce(void *ch);

void CheckKey(void *dummy);

 

/* GetRandom() returns a random integer between min and max. */

#define GetRandom(min, max) ((rand() % (int)(((max) + 1) - (min))) + (min))

 

/* Global repeat flag and video variable */

BOOL repeat = TRUE;

/* Handle for console window */

HANDLE hStdOut;

/* Console information structure */

CONSOLE_SCREEN_BUFFER_INFO csbi;

 

int main(int argc, char *argv[])

{

    CHAR    ch = 'A';

    hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);

if (hStdOut == INVALID_HANDLE_VALUE)

  printf("GetStdHandle() failed, error: %d.\n", GetLastError());

else

  printf("GetStdHandle() is OK.\n");

/* Get display screen's text row and column information. */

if (GetConsoleScreenBufferInfo(hStdOut, &csbi) == 0)

  printf("GetConsoleScreenBufferInfo() failed, error: %d.\n", GetLastError());

else

  printf("GetConsoleScreenBufferInfo() is OK.\n");

printf("--------ENJOY THE SHOW-------\n");

    /* Launch CheckKey() thread to check for terminating keystroke. */

    _beginthread(CheckKey, 0, NULL);

    /* Loop until CheckKey() terminates program. */

    while(repeat)

    {

        /* On first loops, launch character threads. */

        _beginthread(Bounce, 0, (void *) (ch++));

        /* Wait one second between loops. */

        Sleep(1000L);

    }

       return 0;

}

 

/* CheckKey() - Thread to wait for a keystroke, and then clear repeat flag. */

void CheckKey(void *dummy)

{

    printf("Press any key to stop.\n");

    _getch();

    /* _endthread implied */

    repeat = 0;

 

}

 

/* Bounce - Thread to create and control a colored letter that moves

 * around on the screen.

 * Params: ch - the letter to be moved */

void Bounce(void *ch)

{

    /* Generate letter and color attribute from thread argument. */

    char    blankcell = 0x20;

    char    blockcell = (char) ch;

    BOOL    first = TRUE;

   COORD   oldcoord, newcoord;

   DWORD   result;

 

    /* Seed random number generator and get initial location. */

    srand(_threadid);

    printf("Thread ID: %d.\n", _threadid);

    newcoord.X = GetRandom(0, csbi.dwSize.X + 2);

    newcoord.Y = GetRandom(0, csbi.dwSize.Y - 4);

    while(repeat)

    {

        /* Pause between loops. */

        Sleep(100L);

        /* Blank out our old position on the screen, and draw new letter. */

        if(first)

            first = FALSE;

        else

            WriteConsoleOutputCharacter(hStdOut, &blankcell, 1, oldcoord, &result);

       WriteConsoleOutputCharacter(hStdOut, &blockcell, 1, newcoord, &result);

        /* Increment the coordinate for next placement of the block. */

        oldcoord.X = newcoord.X;

        oldcoord.Y = newcoord.Y;

        newcoord.X += GetRandom(-2, 2);

        newcoord.Y += GetRandom(-2, 2);

        /* Correct placement (and beep) if about to go off the screen. */

        if (newcoord.X < 0)

            newcoord.X = 1;

        else if (newcoord.X == csbi.dwSize.X)

            newcoord.X = csbi.dwSize.X - 4;

        else if (newcoord.Y < 0)

            newcoord.Y = 1;

        else if (newcoord.Y == csbi.dwSize.Y)

            newcoord.Y = csbi.dwSize.Y - 4;

        /* If not at a screen border, continue, otherwise beep. */

        else

            continue;

        Beep(((char) ch - 'A') * 100, 175);

    }

    /* _endthread() given to terminate */

    _endthread();

}

 

The output sample:

 

Multithreaded program output sample  

 

Multithreaded program output sample

 

Verifying the threads creation through Windows Task Manager.

 

Verifying multithreaded program execution through Task Manager

 

Another Example

 

The following sample code demonstrates how you can use the thread handle returned by _beginthreadex() with the synchronization API WaitForSingleObject(). The main thread waits for the second thread to terminate before it continues. When the second thread calls _endthreadex(), it causes its thread object to go to the signaled state. This allows the primary thread to continue running. This cannot be done with _beginthread() and _endthread(), because _endthread() calls CloseHandle(), destroying the thread object before it can be set to the signaled state.

 

// mythread.cpp

// compile with: /MT – Multithreaded, Visual C++/.Net

#include <windows.h>

#include <stdio.h>

#include <conio.h>

#include <process.h>

 

unsigned Counter;

 

unsigned __stdcall SecondThreadFunc(void* pArguments)

{

    printf("In second thread...\n");

    while (Counter < 1000000)

        Counter++;

    _endthreadex(0);

    return 0;

}

 

int main(int argc, char *argv[])

{

    HANDLE hThread;

    unsigned threadID;

    printf("Creating second thread...\n");

    printf("Thread ID: %d.\n", threadID);

    // Create the second thread.

    hThread = (HANDLE)_beginthreadex(NULL, 0, &SecondThreadFunc, NULL, 0, &threadID);

    // Wait until second thread terminates. If you comment out the line

    // below, Counter will not be correct because the thread has not

    // terminated, and Counter most likely has not been incremented to

    // 1000000 yet.

    WaitForSingleObject(hThread, INFINITE);

    printf("Counter should be 1000000; it is-> %d\n", Counter);

    // Destroy the thread object.

    CloseHandle(hThread);

       return 0;

}

 

A sample output:

 

Multithreaded program output

 

Other functions and structures definitions used in the previous program examples are presented in the following section.

 

_doserrno, errno, _sys_errlist, and _sys_nerr

 

These global variables hold error codes used by the perror() and strerror() functions for printing error messages. Manifest constants for these variables are declared in STDLIB.H as follows:

 

extern int _doserrno;

extern int errno;

extern char *_sys_errlist[ ];

extern int _sys_nerr;

 

errno is set on an error in a system-level call. Because errno holds the value for the last call that set it, this value may be changed by succeeding calls. Always check errno immediately before and after a call that may set it. All errno values, defined as manifest constants in ERRNO.H, are UNIX-compatible. The values valid for 32-bit Windows applications are a subset of these UNIX values. On an error, errno is not necessarily set to the same value as the error code returned by a system call. For I/O operations only, use _doserrno to access the operating-system error-code equivalents of errno codes. For other operations the value of _doserrno is undefined. Each errno value is associated with an error message that can be printed using perror() or stored in a string using strerror(). perror() and strerror() use the _sys_errlist array and _sys_nerr, the number of elements in _sys_errlist, to process error information. Library math routines set errno by calling _matherr(). To handle math errors differently, write your own routine according to the _matherr reference description and name it _matherr(). The following errno values are compatible with 32-bit Windows applications. Only ERANGE and EDOM are specified in the ANSI standard. Take note that ANSI standard has been superseded by ISO/IEC standard.

 

Constant

System error message

Value

E2BIG

Argument list too long

7

EACCES

Permission denied

13

EAGAIN

No more processes or not enough memory or maximum nesting level reached

11

EBADF

Bad file number

9

ECHILD

No spawned processes

10

EDEADLOCK

Resource deadlock would occur

36

EDOM

Math argument

33

EEXIST

File exists

17

EINVAL

Invalid argument

22

EMFILE

Too many open files

24

ENOENT

No such file or directory

2

ENOEXEC

Exec format error

8

ENOMEM

Not enough memory

12

ENOSPC

No space left on device

28

ERANGE

Result too large

34

EXDEV

Cross-device link

18

 

Table 4

 

 

 

 

 

 

 

 

 

 

 

 

Further reading and digging:

 

  1. Microsoft Visual C++, online MSDN.
  2. For Multibytes, Unicode characters and Localization please refer to Locale, wide characters & Unicode (Story) and Windows users & groups programming tutorials (Implementation).
  3. Structure, enum, union and typedef story can be found C/C++ struct, enum, union & typedef.
  4. Check the best selling C / C++ and Windows books at Amazon.com.

 

 

 

 

 

 

 

 

|< Windows Process & Threads: C Run-Time 3 | Main | Windows Process & Threads: C Run-Time 5 >| Site Index | Download |