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

📄 threads.doc

📁 基于DOS系统下的多任务类库。Class DOS Thread provides a framework for writing DOS applications
💻 DOC
📖 第 1 页 / 共 2 页
字号:

    The parameter N is the DOS code defining the cause of the error.
    "DOSerror" should return a result of "DOSThread::IGNORE" to ignore
    the error, "DOSThread::RETRY" to retry the operation that caused the
    error, or "DOSThread::FAIL" to fail the operation.  Note that during
    critical-error handling, the only DOS services that you can use are
    functions 00 to 0C.  Class DOSThread will intercept and ignore any
    other functions, as they would otherwise cause DOS to crash.

    The function "DOSerror" should never be called directly; it will be
    called automatically if an error occurs during execution of a thread.


    6. Inspecting the status of threads.
    ------------------------------------
    The member function "status" allows you to determine what the
    status of a thread is at any time.  It can be called as follows:

        state = thread1.status ();

    The result is a value of type DOSThread::State, which will be one
    of the following values:

        DOSThread::CREATED      -- the thread is newly created and can
                                   be started by calling "run".
        DOSThread::READY        -- the thread is ready to run (or is
                                   currently running).
        DOSThread::DELAYED      -- the thread has delayed itself by
                                   calling "delay".
        DOSThread::WAITING      -- the thread is waiting to enter a
                                   monitor function guarded by "lock".
        DOSThread::QUEUED       -- the thread is inside a monitor and is
                                   suspended on a monitor queue.
        DOSThread::TERMINATED   -- the thread has terminated.


    7. Using monitors for interthread communication.
    ----------------------------------------------
    One of the problems with multithreaded programs is communicating
    between threads.  Since you do not know when a thread will be
    rescheduled, it is unsafe to modify shared global variables as
    it is perfectly possible for you to be interrupted during the
    process of updating them.  If another thread performs a similar


                                                                Page 6

    update, you may well complete your update using out-of-date
    values when your thread resumes, which means that the global
    variables end up in an inconsistent and incorrect state.

    The base class DOSMonitor provides a basis for developing classes
    which allow safe interthread communication.  All you have to do is
    to derive a class from DOSMonitor which encapsulates any data which
    will be updated by more than one thread and which provides access
    functions to access the data.  Each access function should begin
    by calling the member function "lock" and end by calling "unlock".
    This will guarantee that only one thread at a time is executing an
    access function in any individual monitor.  The general structure
    of a monitor access function is therefore as follows:

        void MyMonitor::access ( /* parameter list */ )
        {
            lock ();
            ...         // access shared data as required
            unlock ();
        }

    Classes derived from DOSMonitor can also contain instances of
    class DOSMonitorQueue.  Within an access function, you can call
    the member function "suspend" with a DOSMonitorQueue as its
    parameter to suspend the thread executing the access function
    until some condition is satisfied.  This will allow other
    threads to execute access functions within that monitor.  The
    other access functions can resume any threads suspended on a
    particular queue by calling the member function "resume"
    with the queue as a parameter.  This will reawaken the threads
    suspended in that queue.

    Note that suspend should be called from within a loop; since
    "resume" will restart all the threads in the specified queue,
    it is not guaranteed that the condition for which the thread
    is waiting will still be true at the time the thread actually
    resumes execution.  Thus to suspend a thread until a counter
    is non-zero, code such as the following should be used:

        while (counter != 0)
            suspend (some_queue);

    As an example, consider a monitor to provide a 20-character
    buffer to transfer data from one thread to another.  It might
    look something like this:

        class Buffer : public DOSMonitor
        {
            char data[20];          // the buffer itself
            int count;              // no. of chars in buffer
            int in;                 // where to put next char
            int out;                // where to get next char from
            DOSMonitorQueue full;
            DOSMonitorQueue empty;
        public:
            Buffer ()               { count = in = out = 0; }
            void get (char& c);     // get a char from the buffer
            void put (char& c);     // put a char in the buffer
        };


                                                                Page 7

    The class constructor initialises "count" to zero to indicate an
    empty buffer and sets "in" and "out" to point to the start of the
    buffer.  Threads must then call "get" and "put" in order to access
    the contents of the buffer.   Two DOSMonitorQueue instances are
    used; "full" is used to suspend threads which call "put" when the
    buffer is full, and "empty" is used to suspend threads which call
    "get" when the buffer is empty.  The code for "get" would be like
    this:

        void Buffer::get (char& c)
        {
            //--- lock the monitor against re-entry
            lock ();

            //--- suspend until the buffer isn't empty
            while (count == 0)
                suspend (empty);

            //--- get next character from the buffer
            c = data [out++];
            out %= 20;

            //--- resume any threads waiting until buffer isn't full
            resume (full);

            //--- unlock the monitor to let other threads in
            unlock ();
        }
     
       
    9. The class "BoundedBuffer".
    -----------------------------
    The class "BoundedBuffer" included in this distribution is a template
    class derived from DOSMonitor which implements a bounded buffer like
    the example above.  You can create a 20-character buffer using this
    class as follows:

        BoundedBuffer<char> buffer(20);

    The type given in angle brackets <...> is the type of item that you
    want to store in the buffer, and the parameter value is the maximum
    number of items the buffer can hold.  The following member functions
    are provided:

        get (item)      -- Get the next item from the buffer and store
                           it in "item".  The function returns 1 (TRUE)
                           if it is successful and 0 (FALSE) if the buffer
                           has been closed (see below).
        put (item)      -- Put a copy of "item" into the buffer.  This
                           function returns 1 (TRUE) if it is successful
                           and 0 (FALSE) if the buffer has been closed
                           (see below).
        items ()        -- Return the number of items in the buffer.
        close ()        -- Close the buffer to prevent further accesses.
                           If you do not close buffers when you have
                           finished using them, you run the risk of your
                           program never terminating -- a thread may be
                           suspended waiting for a character that will
                           never arrive, which means that its destructor
                           will wait forever for it to terminate.


                                                                Page 8

    10. Error handling in monitors.
    -------------------------------
    Monitors derived from class DOSMonitor should provide a virtual
    function called "error" which will be called if any errors are
    detected in a monitor.  "Error" should be declared as follows:

        void error (DOSMonitor::ErrorCode);

    The parameter to "error" is a code for the error which has been
    detected.  This can take any of the following values:

        DOSMonitor::NEW_FAIL        -- there was insufficient memory
                                       to create the necessary data
                                       structures for the monitor.
        DOSMonitor::NO_THREAD       -- a monitor has been called when
                                       there are no threads running.
        DOSMonitor::LOCK_FAIL       -- the current thread is calling
                                       "lock" when it has already
                                       locked the monitor.
        DOSMonitor::UNLOCK_FAIL     -- the current thread has called
                                       "unlock" without having locked
                                       the monitor.
        DOSMonitor::SUSPEND_FAIL    -- the current thread has called
                                       "suspend" without having locked
                                       the monitor.
        DOSMonitor::RESUME_FAIL     -- the current thread has called
                                       "resume" without having locked
                                       the monitor.

    The last five of these indicate a bug in the monitor code which
    should be corrected.  The default action if a monitor does not
    provide a definition for "error" is to exit the program with an
    exit status in the range -1 to -6 (-1 for NEW_FAIL through to -6
    for RESUME_FAIL).


    11. Potential problem areas.
    ----------------------------
    Class DOSTask uses an internal monitor to guard against re-entrant
    calls to DOS, as these are certain to crash your machine.  Direct
    calls to BIOS functions are not protected in the same way.  While
    BIOS calls are generally safer (they use the caller's stack), they
    still manipulate a global shared data area.  It is therefore not
    advisable to call BIOS functions directly, as this can lead to
    hard-to-identify bugs resulting from an inconsistent internal
    state.  However, C++ library functions normally use DOS services
    rather than calling BIOS functions, so most of the functions in
    the standard library are safe to use.  The major exceptions to
    this are the functions defined in <bios.h> and the functions
    "int86" and "int86x".

    If you do need to use BIOS functions directly, the best approach
    to adopt is to localise all BIOS calls in a single monitor so that
    only one task at a time can call a BIOS function; however, since
    DOS services will perform their functions by making BIOS calls,
    you must also use the monitor to encapsulate all DOS calls to
    guarantee that only one task at a time is making a BIOS call.
    This may not be a terribly practical solution.


                                                                Page 9

    Another point worth noting is that screen output is best done
    using "fputs" rather than "cout", "printf" or "puts".  Each of
    these generates several DOS calls to generate their output, and
    it is therefore possible for another thread to interleave some
    other output with it.  In particular, if you use "cout" it is
    possible for the same output to appear twice if the thread is
    interrupted after the output has been displayed but before the
    internal buffer has been cleared.  The next thread which uses
    "cout" will have its output appended to the existing contents
    of the buffer which will then be displayed in its entirety.

    A more serious problem (which I have been completely unable to
    resolve) is that programs which use direct BIOS calls can crash
    the system if high memory is being used.  If your program needs
    to use direct BIOS calls, you should only do this if you are
    NOT using an upper memory manager such as EMM386 or QEMM.  There
    is obviously some memory management context information which
    needs to be saved on a thread context switch, but without any
    knowledge of the internal workings of upper memory managers I
    do not know how to proceed on this (and if anyone can help me
    here, I will be eternally grateful!).


    12. A plea for feedback.
    ------------------------
    If you use this class, please contact the author via the addresses
    at the beginning; if you don't have e-mail access please send me a
    postcard (I like postcards!) just to let me know you've looked at
    it.  Feel free to suggest enhancements, find bugs or (better still)
    fix them and send me patches.  Happy hacking!

⌨️ 快捷键说明

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