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

#include "zthread/ThreadedExecutor.h"

#include

using namespace ZThread;

using namespace std;

class Count {

  static const int sz = 100;

  int n[sz];

public:

  void increment() {

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

      n[i]++;

  }

};

class Incrementer : public Runnable {

  Count* count;

public:

  Incrementer(Count* c) : count(c) {}

  void run() {

    for(int n = 100; n > 0; n--) {

      Thread::sleep(250);

      count->increment();

    }

  }

};

int main() {

  cout << "This will cause a segmentation fault!" << endl;

  Count count;

  try {

    Thread t0(new Incrementer(&count));

    Thread t1(new Incrementer(&count));

  } catch(Synchronization_Exception& e) {

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

  }

} ///:~

The Count class may seem like overkill at first, but if n is only a single int (rather than an array), the compiler can put it into a register and that storage will still be available (albeit technically illegal) after the Count object goes out of scope. It’s difficult to detect the memory violation in that case. Your results may vary depending on your compiler and operating system, but try making it n single int and see what happens. In any event, if Count contains an array of ints as above, the compiler is forced to put it on the stack and not in a register.

Incrementer is a simple task that uses a Count object. In main( ), you can see that the Incrementer tasks are running for long enough that the Count object will go out of scope, and so the tasks try to access an object that no longer exists. This produces a program fault.

To fix the problem, we must guarantee that any objects shared between tasks will be around as long as those tasks need them. (If the objects were not shared, they could be composed directly into the task’s class and thus tie their lifetime to that task.) Since we don’t want the static program scope to control the lifetime of the object, we put the object on the heap. And to make sure that the object is not destroyed until there are no other objects (tasks, in this case) using it, we use reference counting.

Reference counting was explained thoroughly in volume one of this book and further revisited in this volume. The ZThread library includes a template called CountedPtr that automatically performs reference counting and deletes an object when the reference count goes to zero. Here’s the above program modified to use CountedPtr to prevent the fault:

//: C11:ReferenceCounting.cpp

// A CountedPtr prevents too-early destruction.

//{L} ZThread

#include "zthread/Thread.h"

#include "zthread/CountedPtr.h"

#include

using namespace ZThread;

using namespace std;

class Count {

  static const int sz = 100;

  int n[sz];

public:

  void increment() {

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

      n[i]++;

  }

};

class Incrementer : public Runnable {

  CountedPtr count;

public:

  Incrementer(const CountedPtr& c ) : count(c) {}

  void run() {

    for(int n = 100; n > 0; n--) {

      Thread::sleep(250);

      count->increment();

    }

  }

};

int main() {

  CountedPtr count(new Count);

  try {

    Thread t0(new Incrementer(count));

    Thread t1(new Incrementer(count));

  } catch(Synchronization_Exception& e) {

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

  }

} ///:~

Incrementer now contains a CountedPtr object, which manages a Count. In main( ), the CountedPtr objects are passed into the two Incrementer objects by value, so the copy-constructor is called, increasing the reference count. As long as the tasks are still running, the reference count will be nonzero, and so the Count object managed by the CountedPtr will not be destroyed. Only when all the tasks using the Count are completed will delete be called (automatically) on the Count object by the CountedPtr.

Whenever you have objects that are used by more than one task, you’ll almost always need to manage those objects using the CountedPtr template in order to prevent problems arising from object lifetime issues.

<p>Improperly accessing resources</p>

Consider the following example in which one task generates even numbers and other tasks consume those numbers. In this case, the only job of the consumer threads is to check the validity of the even numbers.

We’ll first define EvenChecker, the consumer thread, since it will be reused in all the subsequent examples. To decouple EvenChecker from the various types of generators that we will experiment with, we’ll create an interface called Generator, which contains the minimum necessary functions that EvenChecker must know about: that it has a nextValue( ) function and that it can be canceled.

//: C11:EvenChecker.h

#ifndef EVENCHECKER_H

#define EVENCHECKER_H

#include "zthread/CountedPtr.h"

#include "zthread/Thread.h"

#include "zthread/Cancelable.h"

#include "zthread/ThreadedExecutor.h"

#include

class Generator : public ZThread::Cancelable {

  bool canceled;

public:

  Generator() : canceled(false) {}

  virtual int nextValue() = 0;

  void cancel() { canceled = true; }

  bool isCanceled() { return canceled; }

};

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

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

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

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

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

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

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

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

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