⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 threads.doc

📁 多任务操作系统控制的DOS环境下的实现的C语言源程序。 利用时间片的方式
💻 DOC
📖 第 1 页 / 共 2 页
字号:


                                                                Page 1

    Class DOSThread: a base class for multithreaded DOS programs.
    ---------------------------------------------------------------------

      Author: John English (je@unix.brighton.ac.uk)
              Department of Computing
              University of Brighton
              Brighton BN2 4GJ, England.

      Copyright (c) J.English 1993.

      Permission is granted to use copy and distribute the
      information contained in this file provided that this
      copyright notice is retained intact and that any software
      or other document incorporating this file or parts thereof
      makes the source code for the library of which this file
      is a part freely available.


    1. Introduction.
    ----------------
    Class DOSThread provides a framework for writing DOS applications
    which consist of multiple (pseudo-)parallel "threads" of execution.
    DOS was not designed as a multithreading operating system (in fact,
    it actively hinders multithreading by being non-reentrant) but this
    class allows you to create multithreaded DOS applications without
    worrying about such problems (although you should read section 11
    for some important caveats concerning direct calls to the BIOS).

    To create a thread using this class, you must derive a class from
    it which contains the code you want to be executed in a member
    function called "main".  All you have to do then is declare an
    instance of your derived class and then call the member function
    "run" to start it running.  Each thread will be executed in bursts
    of 1 clock tick (55ms) at a time before being suspended to allow
    the other threads a chance to execute.  The length of the timeslice
    can be changed if necessary using the member function "timeslice".
    If required, timeslicing can be disabled entirely, in which case
    it is up to each thread to relinquish the use of the processor at
    regular intervals so that the other threads get a chance to run.

    Threads can delay themselves for a given number of clock ticks by
    using the member function "delay", they can relinquish the use of
    the processor to allow other threads to execute by using the member
    function "pause", and they can terminate themselves or each other
    by using the member function "terminate".  It is also possible to
    inspect the current state of any thread (ready-to-run, terminated,
    delayed and so on) by using the member function "status" and to
    wait for a thread to terminate by using the member function "wait".

    Two additional classes (DOSMonitor and DOSMonitorQueue) allow you
    to derive monitor classes of your own to facilitate communication
    between threads.  A monitor will normally contain data structures
    which can be accessed by several threads.  You can guarantee that
    only one thread at a time is executing a monitor member function
    which accesses the data by calling the member function "lock" at
    the start of the monitor function.  If any other thread is already
    executing a monitor function guarded by a call to "lock", the
    current thread will wait until it is safe to proceed.  At the end


                                                                Page 2

    of the monitor function, you should call "unlock" to allow any
    waiting threads to proceed.  Monitors can also contain instances
    of DOSMonitorQueue which allow threads to suspend themselves in a
    monitor function until some condition has been fulfilled (e.g.
    that a buffer isn't empty).  Some other thread executing within
    the monitor can resume any suspended threads when the condition
    is fulfilled (e.g. after a data item has been put into an empty
    buffer).  A template class which implements a bounded buffer is
    included in this distribution.  This is probably the commonest
    use of monitors in most applications, so it may well not be
    necessary to define any other monitor classes of your own.

    If you find this class useful or have any suggestions as to how it
    can be enhanced, please contact the author at one of the addresses
    given above.  E-mail and postcards will both be welcome!


    2. Deriving a new thread "MyThread" from class DOSThread.
    ---------------------------------------------------------
    Every thread is created by deriving a new class from the base class
    DOSThread.  Each derived thread class must provide a definition of
    a member function called "main" which contains the code which the
    thread will execute.  "Main" is declared like this:

        void MyThread::main ()
        {
            // code to be executed by your thread
        }

    The constructor for your derived class "MyThread" will invoke the
    constructor for DOSThread.  The constructor for DOSThread requires
    a single unsigned integer parameter which specifies the size of the
    stack to be allocated for the thread.  However, a default of 2048
    bytes is assumed, and if this is sufficient you need not explicitly
    call the DOSThread constructor at all.

    Having created a derived thread class, you can then declare instances
    of this class in your program, as for example:

        MyThread thread1;           // a thread called "thread1"
        MyThread threads [5];       // five identical threads

    The threads you declare will not be executed until you call the member
    function "run", as follows:

        thread1.run ();

    "Run" returns a result to the calling program which is TRUE (1)
    if the thread was started successfully, and FALSE (0) if it could
    not be started (either because there was insufficient memory to
    create the necessary data structures or because it has already
    been started).  Note that you cannot call "run" from your thread
    constructor since the virtual function "main" is not accessible
    until you have finished executing the constructor.

    Once a thread has been started successfully, it will be executed
    in parallel with the main program.  The main program effectively
    becomes another thread (although it has no name, and it can only


                                                                Page 3

    make use of the static functions "pause" and "delay" described
    below).

    The default is for each thread to be granted a "timeslice" of one
    clock tick (55ms).  If a thread is still running when its timeslice
    expires, it is moved to the back of the queue of ready-to-run threads
    and execution of the next thread in the queue is then resumed.  The
    static member function "timeslice" can be used to change the length
    of the timeslices used.  "Timeslice" requires an unsigned integer
    parameter specifying the desired timeslice length in clock ticks,
    as for example:

        DOSThread::timeslice (18);    // timeslice once a second (18 x 55ms)

    If the parameter is zero, timeslicing is disabled.  In this case
    it is up to individual threads to relinquish control to each other
    by calling a member function which will cause another thread to be
    scheduled.  A member function "pause" is provided for just this
    purpose, and is described below.

    "Timeslice" must be called before any threads are declared; as soon
    as the first thread has been declared, calls to "timeslice" will be
    ignored.  This means you cannot dynamically change the length of the
    timeslice during execution of the program.


    3. Writing the member function "main".
    --------------------------------------
    "MyThread::main" (the main function of your derived class) will be
    executed in parallel with the rest of the program once it has been
    started by calling "run" as described above.  While "MyThread::main"
    can be written in exactly the same way as any other function, it is
    important to remember that it is sharing the processor with a number
    of other threads and that if it has nothing useful to do, it should
    allow some other thread to run.  The member function "pause" lets you
    temporarily release the processor to another thread:

        pause ();                   // schedule another thread

    This is a static member function, so is can be called from any
    point in a program as "DOSThread::pause".  Even if you are using
    timeslicing, it is a good idea to call "pause" if your thread is
    temporarily unable to proceed (e.g. it is waiting for a key to
    be pressed), as otherwise it will do nothing useful for several
    milliseconds until its timeslice expires and another thread gets
    a chance to run.

    You can also make your thread wait for a fixed time by using the
    static member function "delay", specifying the delay period as
    a number of 55ms clock ticks:

        delay (18);                 // delay for 1 second (18 x 55ms)

    Note that "pause" and "delay" are both static member functions
    which always affect the current thread.  This means that you are
    not able to "pause" or "delay" any other thread.  It also means
    that you can call these functions from the main program if you
    need to.


                                                                Page 4

    When "MyThread::main" returns, the thread terminates.  You can also
    terminate a thread explicitly using the member function "terminate".
    If another thread (or the main program) wants to terminate "thread1",
    it can do it like this:

        thread1.terminate ();

    This is potentially problematical, as you have no idea what "thread1"
    is doing at the time.  A thread can also terminate itself:

        terminate ();

    which has the same effect as returning from the main function of
    the thread.


    4. Initialisation and finalisation.
    -----------------------------------
    When a thread is declared by the main program or by another thread
    the constructor for class DOSThread is called to create the thread
    and any constructor defined by your derived thread class is then
    called to complete the initialisation.  Note that a thread is not
    completely constructed until this sequence is complete, and in
    particular this means that you cannot call "run" from inside your
    derived class constructor to start the thread running immediately.

    When you reach the end of a block in which a thread was declared,
    the destructor for the thread will be called.  Any destructor you
    provide in your derived class is called first (while the thread
    could still be running), and the standard DOSThread destructor is
    then called to wait for the thread to terminate before tidying up.
    This means that your destructor should not do anything which might
    cause the thread to fail.  The member function "wait" allows you to
    wait for the thread to terminate, and your destructor should call
    this function before doing anything that might cause the thread to
    fail.  In other words, your destructor should be written like this:

        MyThread::~MyThread ()
        {
            wait ();            // wait for thread to terminate
            ...                 // do any class-specific tidying up
        }


    5. Handling "control-break" and critical errors.
    ------------------------------------------------
    Class DOSThread provides a simple mechanism for dealing with events
    reported by DOS.  The first such event is the "control-break" key
    being pressed to abort a program.  Class DOSThread intercepts these
    events and sets an internal flag.  Individual threads (or the main
    program) can call the static member function "userbreak" to test
    if control-break has been pressed:

        if (DOSThread::userbreak ()) ...

    The flag will remain set so that other threads can also inspect it.
    Alternatively, you can use the static function "cancelbreak", which
    is identical to "userbreak" except that it also resets the internal


                                                                Page 5

    flag.  This allows an individual thread to deal with a control-break
    event without any other threads being able to deal with the same event
    as well as providing a means for resetting the flag.  If threads do
    not use either of these functions, control-breaks will be ignored
    completely.

    Critical errors (the familiar "Abort, Retry, Fail?" errors) can be
    generated by DOS if a disk is write protected or a printer is offline.
    Classes derived from DOSThread can provide a virtual function "error"
    to deal with any critical errors they may generate.  Threads provide
    their own critical error handlers on an individual basis; the default
    handler just fails the operation.  To provide a critical error handler
    for a thread class, define a member function "DOSerror" as follows:

        DOSThread::Error DOSerror (int N);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -