Jump to content

Using Threads: Difference between revisions

From EDM2
Added reference section
No edit summary
 
(13 intermediate revisions by 4 users not shown)
Line 1: Line 1:
== Rationale ==
== Rationale ==
After reading the Pthreads Primer, and attempting to apply it to eCS, I found a need for an introduction to the API and explanations on how to use it.  This is an attempt to fill that void.  --[[User:Myrkraverk|Myrkraverk]] 03:19, 4 November 2007 (CET)


After reading the Pthreads Primer, and attmepting to apply it to eCS, I found a need for an introduction to the API and explanations on how to use it. This is an attempt to fill that void. --[[User:Myrkraverk|Myrkraverk]] 03:19, 4 November 2007 (CET)
== Use of C++ in the Examples ==
I am a C++ programmer and frequently run into "small differences" when attempting to use a plain C compiler. These are mostly differences in automatic variable declaration and initialization which are leveraged somewhat with C99. However, I do not want to spend my time battling "plain C" syntax peculiarities or C99 feature command line switches, so I simply compile all my examples as C++.
 
The only "especially C++" feature I use is std::cout instead of printf() which any reasonably proficient C programmer can translate on the spot.


== Creating Threads ==
== Creating Threads ==
Thread creation can be done with the [[DosCreateThread]]() system call, or the C runtime library [[_beginthread]]() function.


There are two basic ways to create threads using the OS/2 API, [[OS2 API:DosCreateThread | DosCreateThread()]] and [[OS2 API:CLR:_beginthread | _beginthread()]]. However, note the following from the OpenWatcom C/C++ Programmers Guide:
The DosCreateThread() system call is inherently low level and does not initialize the C/C++ runtime environment. In particular:
 
* You can not assume C++ Exceptions will work. VisualAge C++ has #pragma handler for this but it '''can not be relied upon''' with OpenWatcom or GCC.
: '''WARNING!'''  If any thread calls a library function, you must use the _beginthread function to create the thread. Do not use the DosCreateThread API function.
* You can not rely on the state of the floating point unit. You can however, reset it with <tt>_fpreset()</tt>.
 
* You can not rely on [[DosExit]]() to clear up thread specific data and/or the runtime environment; use <tt>_endthread()</tt> instead.
This makes me want to use _beginthread() all the time, and leave DosCreateThread() to other people.
: That is, the C/C++ runtime environment may be initialized on first use (this is documented for VisualAge C++ but apparently not for OpenWatcom or GCC). Also:
:: From the OpenWatcom C/C++ Programmer's Guide:
::: '''WARNING!'''  If any thread calls a library function, you must use the _beginthread function to create the thread. Do not use the DosCreateThread API function.


There are some differences, between the two calls, which affect how you use them.  They are not really interchangable.  The most notable differences are the way they return the thread ID to the calling thread, and the parameters passed to the newly created thread are not identical.  The former is just a matter of moving parameters around, when changing one call to the other, the other can cause compiler errors and conversion headaces that can propagate throughout the application, which can be quite a nightmare, if a change from one to the other is made at a late stage in development.
:: From the OS/2 Toolkit 4.5:
::: '''Note:''' If you use DosCreateThread, you must explicitly call _endthread to terminate the thread.


Here is a short example:
None of this is an issue when using <tt>_beginthread()</tt>; it takes care of setting up the runtime environment and tearing it down when the thread function ends.


==== Hello, Threaded World! ====
Here is a short example of <tt>_beginthread()</tt>:
  // file: hello_thread.c++
  // file: hello_thread.c++
  #include <iostream>
  #include <iostream>
Line 29: Line 39:
  int main( int argc, char *argv[] )
  int main( int argc, char *argv[] )
  {
  {
   _beginthread( hello, 0, 4096, 0 );
   _beginthread( hello, 0, 4096 * 10, 0 );
   DosSleep( 10 );
   DosSleep( 10 );                       // Allow the thread to finish.
  }
  }


This can be compiled with OpenWatcom like so:
This can be compiled with OpenWatcom like so:
  >wcl386 -cc++ -bm "hello_thread.c++"
  >wcl386 -cc++ -bm "hello_thread.c++"
or with GCC like so:
or with GCC like so:
  >g++ -Zmt "hello_thread.c++"
  >g++ -Zmt "hello_thread.c++"
 
Here is the same program, using DosCreateThread(). Note that it uses a library call, despite the warnings above.
Here is the same program, using DosCreateThread().
 
'''NOTE:''' This program deliberately disregards 2 reccommendations:
 
* It uses std::cout in a thread created by DosCreateThread() as per the notice above.
* It does not end the thread with _endthread() as per the notice below.
 
From the OS/2 Toolkit 4.5 C Library Reference:
 
: '''Note:''' If you use DosCreateThread, you must explicitly call _endthread to terminate the thread.  


  // file: hello_doscreate.c++
  // file: hello_doscreate.c++
Line 67: Line 64:
   TID tid = 0;
   TID tid = 0;
   DosCreateThread( &tid, hello, 0, CREATE_READY | STACK_SPARSE, 4096 * 10 );
   DosCreateThread( &tid, hello, 0, CREATE_READY | STACK_SPARSE, 4096 * 10 );
   DosSleep( 10 );
   DosSleep( 10 );   // Allow the thread to finish
  }
  }
Despite disregarding both of the notices above, it works.  If you look closely (and have looked at the relevant function prototypes in the Control Program Programming Guide and Reference (CPPGR), C Library Reference (CLR) and/or OpenWatcom C/C++ Programmers Guide (OWCPG), this program uses 10 times the stack size of the former.  This is becouse it results in stack overflow, when compiled with OpenWatcom.  It works with 4K stack space in GCC.


== References ==
== References ==
 
* [[UsingThreads:SynchronizationTimings | Synchronization Timings]]
* [[OS2 API | OS/2 API Project]]
* [http://www.openwatcom.org/ftp/manuals/1.5/pguide.pdf OpenWatcom C/C++ Programmer's Guide] (pdf) - Links to version 1.5 of the manual.
* [http://www.openwatcom.org/ftp/manuals/1.5/pguide.pdf OpenWatcom C/C++ Programmer's Guide] (pdf) - Links to version 1.5 of the manual.
* [http://www.openwatcom.org/ftp/manuals/1.5/clib.pdf OpenWatcom C Library Reference] (pdf) - Links to version 1.5 of the manual.
* [http://www.openwatcom.org/ftp/manuals/1.5/clib.pdf OpenWatcom C Library Reference] (pdf) - Links to version 1.5 of the manual.
Line 80: Line 74:
** Control Program Programming Guide and Reference
** Control Program Programming Guide and Reference
** C Library Reference
** C Library Reference
* {{FileLink|EMX_0-9d.zip}}. EMX 0.9d Documentation on /book (inf).
[[Category:C++ Articles]]

Latest revision as of 17:09, 27 January 2024

Rationale

After reading the Pthreads Primer, and attempting to apply it to eCS, I found a need for an introduction to the API and explanations on how to use it. This is an attempt to fill that void. --Myrkraverk 03:19, 4 November 2007 (CET)

Use of C++ in the Examples

I am a C++ programmer and frequently run into "small differences" when attempting to use a plain C compiler. These are mostly differences in automatic variable declaration and initialization which are leveraged somewhat with C99. However, I do not want to spend my time battling "plain C" syntax peculiarities or C99 feature command line switches, so I simply compile all my examples as C++.

The only "especially C++" feature I use is std::cout instead of printf() which any reasonably proficient C programmer can translate on the spot.

Creating Threads

Thread creation can be done with the DosCreateThread() system call, or the C runtime library _beginthread() function.

The DosCreateThread() system call is inherently low level and does not initialize the C/C++ runtime environment. In particular:

  • You can not assume C++ Exceptions will work. VisualAge C++ has #pragma handler for this but it can not be relied upon with OpenWatcom or GCC.
  • You can not rely on the state of the floating point unit. You can however, reset it with _fpreset().
  • You can not rely on DosExit() to clear up thread specific data and/or the runtime environment; use _endthread() instead.
That is, the C/C++ runtime environment may be initialized on first use (this is documented for VisualAge C++ but apparently not for OpenWatcom or GCC). Also:
From the OpenWatcom C/C++ Programmer's Guide:
WARNING! If any thread calls a library function, you must use the _beginthread function to create the thread. Do not use the DosCreateThread API function.
From the OS/2 Toolkit 4.5:
Note: If you use DosCreateThread, you must explicitly call _endthread to terminate the thread.

None of this is an issue when using _beginthread(); it takes care of setting up the runtime environment and tearing it down when the thread function ends.

Hello, Threaded World!

Here is a short example of _beginthread():

// file: hello_thread.c++
#include <iostream>
#include <process.h>

#define INCL_DOSPROCESS
#include <os2.h>

void hello( void * )
{
  std::cout << "Hello from thread." << std::endl;
}

int main( int argc, char *argv[] )
{
  _beginthread( hello, 0, 4096 * 10, 0 );
  DosSleep( 10 );                       // Allow the thread to finish.
}

This can be compiled with OpenWatcom like so:

>wcl386 -cc++ -bm "hello_thread.c++"

or with GCC like so:

>g++ -Zmt "hello_thread.c++"

Here is the same program, using DosCreateThread(). Note that it uses a library call, despite the warnings above.

// file: hello_doscreate.c++
#include <iostream>

#define INCL_DOSPROCESS
#include <os2.h>

void _System hello( long unsigned int )
{
  std::cout << "Hello from thread." << std::endl;
}

int main( int argc, char *argv[] )
{
  TID tid = 0;
  DosCreateThread( &tid, hello, 0, CREATE_READY | STACK_SPARSE, 4096 * 10 );
  DosSleep( 10 );   // Allow the thread to finish
}

References