Читаем Thinking In C++. Volume 2: Practical Programming полностью

  void run() {

    try {

      while(!Thread::interrupted()) {

        {

          Guard g(lock);

          while(!gotToast)

            toastReady.wait();

          buttered++;

        }

        cout << "Buttering toast " << buttered << endl;

        jammer->moreButteredToastReady();

        {

          Guard g(lock);

          gotToast = false;

        }

      }

    } catch(Interrupted_Exception&) { /* Exit */ }

    cout << "Butterer off" << endl;

  }

};

class Toaster : public Runnable {

  CountedPtr butterer;

  int toasted;

public:

  Toaster(CountedPtr& b) : butterer(b) {

    toasted = 0;

    srand(time(0)); // Seed the random number generator

  }

  void run() {

    try {

      while(!Thread::interrupted()) {

        Thread::sleep(rand()/(RAND_MAX/5)*100);

        // ...

        // Create new toast

        // ...

        cout << "New toast " << ++toasted << endl;

        butterer->moreToastReady();

      }

    } catch(Interrupted_Exception&) { /* Exit */ }

    cout << "Toaster off" << endl;

  }

};

int main() {

  try {

    cout << "Press to quit" << endl;

    CountedPtr jammer(new Jammer);

    CountedPtr butterer(new Butterer(jammer));

    ThreadedExecutor executor;

    executor.execute(new Toaster(butterer));

    executor.execute(butterer);

    executor.execute(jammer);

    cin.get();

    executor.interrupt();

  } catch(Synchronization_Exception& e) {

    cerr << e.what() << endl;

  }

} ///:~

The classes are defined in the reverse order that they operate to simplify forward-referencing issues.

Jammer and Butterer both contain a Mutex, a Condition, and some kind of internal state information that changes to indicate that the process should suspend or resume. (Toaster doesn’t need these since it is the producer and doesn’t have to wait on anything.) The two run( ) functions perform an operation, set a state flag, and then call wait( ) to suspend the task. The moreToastReady( ) and moreButteredToastReady( ) functions change their respective state flags to indicate that something has changed and the process should consider resuming and then call signal( ) to wake up the thread.

The difference between this example and the previous one is that, at least conceptually, something is being produced here: toast. The rate of toast production is randomized a bit, to add some uncertainty. And you’ll see that when you run the program, things aren’t going right, because many pieces of toast appear to be getting dropped on the floor—not buttered, not jammed.

<p>Solving threading problems with queues</p>

Often, threading problems are based on the need for tasks to be serialized—that is, to take care of things in order. ToastOMatic.cpp must not only take care of things in order, it must be able to work on one piece of toast without worrying that toast is falling on the floor in the meantime. You can solve many threading problems by using a queue that synchronizes access to the elements within:

//: C11:TQueue.h

#ifndef TQUEUE_H

#define TQUEUE_H

#include "zthread/Thread.h"

#include "zthread/Condition.h"

#include "zthread/Mutex.h"

#include "zthread/Guard.h"

#include

template class TQueue {

  ZThread::Mutex lock;

  ZThread::Condition cond;

  std::deque data;

public:

  TQueue() : cond(lock) {}

  void put(T item) {

    ZThread::Guard g(lock);

    data.push_back(item);

    cond.signal();

  }

  T get() {

    ZThread::Guard g(lock);

    while(data.empty())

      cond.wait();

    T returnVal = data.front();

    data.pop_front();

    return returnVal;

  }

};

#endif // TQUEUE_H ///:~

This builds on the Standard C++ Library deque by adding:

1.Synchronization to ensure that no two threads add objects at the same time.

2.wait( ) and signal( ) so that a consumer thread will automatically suspend if the queue is empty, and resume when more elements become available.

This relatively small amount of code can solve a remarkable number of problems.

Here’s a simple test that serializes the execution of LiftOff objects. The consumer is LiftOffRunner, which pulls each LiftOff object off the TQueue and runs it directly. (That is, it uses its own thread by calling run( ) explicitly rather than starting up a new thread for each task.)

//: C11:TestTQueue.cpp

//{L} ZThread

#include

#include

#include "TQueue.h"

#include "zthread/Thread.h"

#include "LiftOff.h"

using namespace ZThread;

using namespace std;

class LiftOffRunner : public Runnable {

  TQueue rockets;

public:

  void add(LiftOff* lo) { rockets.put(lo); }

  void run() {

    try {

      while(!Thread::interrupted()) {

        LiftOff* rocket = rockets.get();

        rocket->run();

      }

    } catch(Interrupted_Exception&) { /* Exit */ }

    cout << "Exiting LiftOffRunner" << endl;

  }

};

int main() {

  try {

    LiftOffRunner* lor = new LiftOffRunner;

    Thread t(lor);

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

      lor->add(new LiftOff(10, i));

    cin.get();

    lor->add(new LiftOff(10, 99));

    cin.get();

    t.interrupt();

  } catch(Synchronization_Exception& e) {

    cerr << e.what() << endl;

  }

} ///:~

Перейти на страницу:

Похожие книги

1С: Бухгалтерия 8 с нуля
1С: Бухгалтерия 8 с нуля

Книга содержит полное описание приемов и методов работы с программой 1С:Бухгалтерия 8. Рассматривается автоматизация всех основных участков бухгалтерии: учет наличных и безналичных денежных средств, основных средств и НМА, прихода и расхода товарно-материальных ценностей, зарплаты, производства. Описано, как вводить исходные данные, заполнять справочники и каталоги, работать с первичными документами, проводить их по учету, формировать разнообразные отчеты, выводить данные на печать, настраивать программу и использовать ее сервисные функции. Каждый урок содержит подробное описание рассматриваемой темы с детальным разбором и иллюстрированием всех этапов.Для широкого круга пользователей.

Алексей Анатольевич Гладкий

Программирование, программы, базы данных / Программное обеспечение / Бухучет и аудит / Финансы и бизнес / Книги по IT / Словари и Энциклопедии
1С: Управление торговлей 8.2
1С: Управление торговлей 8.2

Современные торговые предприятия предлагают своим клиентам широчайший ассортимент товаров, который исчисляется тысячами и десятками тысяч наименований. Причем многие позиции могут реализовываться на разных условиях: предоплата, отсрочка платежи, скидка, наценка, объем партии, и т.д. Клиенты зачастую делятся на категории – VIP-клиент, обычный клиент, постоянный клиент, мелкооптовый клиент, и т.д. Товарные позиции могут комплектоваться и разукомплектовываться, многие товары подлежат обязательной сертификации и гигиеническим исследованиям, некондиционные позиции необходимо списывать, на складах периодически должна проводиться инвентаризация, каждая компания должна иметь свою маркетинговую политику и т.д., вообщем – современное торговое предприятие представляет живой организм, находящийся в постоянном движении.Очевидно, что вся эта кипучая деятельность требует автоматизации. Для решения этой задачи существуют специальные программные средства, и в этой книге мы познакомим вам с самым популярным продуктом, предназначенным для автоматизации деятельности торгового предприятия – «1С Управление торговлей», которое реализовано на новейшей технологической платформе версии 1С 8.2.

Алексей Анатольевич Гладкий

Финансы / Программирование, программы, базы данных