Advanced   Java   Services std::thread  C++ 11


Custom Thread Classes

Es werden leicht verschiedene Ansätze gezeigt.


Beispiel 1

Mit der folgenden Klasse gelingt es, Threads später zu starten und auch länger laufen zu lassen als main.

/*
   Legt ein Threadobject an, ohne einen Thread zu starten, das gelingt mit dem
   Defaultkonstruktor von std::thread. Der eigentliche Thread wird dann der
   start-Methode übergeben. Diese bringt diesen Thread mit move oder swap in
   den privaten Datenteil. Über die start-Methode kann jeder beliebige std::thread
   übergeben werden. Diese arbeiten mit beliebigen Funktionen. Ein join() im
   Destruktor sorgt dafür, daß am Ende aufgeräumt wird. Hier kann es passieren, daß der
   Thread länger lebt als main.
*/
class Thread1
{
private:
   std::thread th;

public:
   Thread1()
   {};
   virtual ~Thread1()
   {
      std::cout << "destruktor-id" << std::this_thread::get_id() << std::endl;
      this->th.join();
      std::cout << "destruktor-end-id" << std::this_thread::get_id() << std::endl;

   };

   // startet einen thread der eine externe funktion vewendet
   void start(std::thread th)
   {
      this->th.swap(th);  // geht beides
      //this->th = std::move(th);
   };
};

Die folgende Methode soll in einem eigenen Thread laufen

/*
   wird von test_Thread1 verwendet
*/
void sayHello(int howOften)
{
   for(int i=0; i<howOften; i++ )
   {
      cout << "   hello-id" << std::this_thread::get_id()  << endl;
      std::this_thread::sleep_for (std::chrono::seconds(1));
   }
};

Die folgende Methode legt ein Objekt vom Typ Thread1 an und startet den Thread.

void test_Thread1()
{
   cout << "Thread1 example\n" << endl; //

   Thread1 thread1;  // leerer Thread
   for(int i=0; i<3; i++ )
   {
      cout << "main-id" << std::this_thread::get_id()  << endl;
      std::this_thread::sleep_for (std::chrono::seconds(1));
   }
   // anonaymes std::thread Objekt wird übergeben
   thread1.start( std::thread(sayHello, 7)) ;  // muß so verwendet werden
   for(int i=0; i<3; i++ )
   {
      cout << "main-id" << std::this_thread::get_id()  << endl;
      std::this_thread::sleep_for (std::chrono::seconds(1));
   }
   cout << "main-end" << endl;
}

Zwei Ausgaben




Beispiel 2

Thread2 hat eine Memberfunction run(), die in einem Thread laufen soll. Mit Hilfe von std::move() gelingt das. Damit der Thread nur einmal gestartet werden kann gibt es eine Abfrage in start().

/*
Thread2 hat eine Memberfunction run(), die in einem Thread laufen soll.
Mit Hilfe von std::move() gelingt das. Damit der Thread nur einmal gestartet
werden kann gibt es eine Abfrage in start().
*/
class Thread2
{
private:
std::thread th;
bool running = false;

public:
Thread2()
{};
virtual ~Thread2()
{
   std::cout << "destruktor-id" << std::this_thread::get_id() << std::endl;
   this->th.join();
   std::cout << "destruktor-end-id" << std::this_thread::get_id() << std::endl;
};

void start()  // darf nur einmal passieren
{
   if (running == false)
   {
      this->running = true;
      this->th = std::move( std::thread(&Thread2::run, this) );  //
      // swap geht hier nicht
   }
};

void run()
{
   for(int i=0; i<7; i++ )
   {
      std::cout << "run-id" << std::this_thread::get_id() << std::endl;
      std::this_thread::sleep_for (std::chrono::seconds(1));
   }
};

Die Testmethode

/*
   Testet die Klasse Thread2. Die Klasse verwendet eine eigene Memberfunction
   als Threadmethode
*/
void test_Thread2()
{
   cout << "Thread2 example\n" << endl; //

   Thread2 thr;
   for(int i=0; i<3; i++ )
   {
      cout << "main-id" << std::this_thread::get_id()  << endl;
      std::this_thread::sleep_for (std::chrono::seconds(1));
   }
   thr.start() ;  //
   for(int i=0; i<3; i++ )
   {
      cout << "main-id" << std::this_thread::get_id()  << endl;
      std::this_thread::sleep_for (std::chrono::seconds(1));
   }
   cout << "main-end" << endl;
}

Zwei Ausgaben


Beispiel 3

Beispiel 3 ist eine Variation von Beispiel 2 und zeigt, daß die interne Methode run() auch mit Parametern aufgerufen werden kann.

/*
   Variation von Thread2
   Thread3 hat eine Memberfunction run(), mit zwei Parametern.
*/
class Thread3
{
private:
   std::thread th;
   bool running = false;

public:
   Thread3()
   {};
   virtual ~Thread3()
   {
      std::cout << "destruktor-id" << std::this_thread::get_id() << std::endl;
      this->th.join();
      std::cout << "destruktor-end-id" << std::this_thread::get_id() << std::endl;
   };

   void start(int data, int data2)  // darf nur einmal passieren
   {
      if (running == false)
      {
         this->running = true;
         this->th = std::move( std::thread(&Thread3::run, this, data, data2) );  //
         // swap geht hier nicht
      }
   };

   void run(int data, int data2)
   {
      for(int i=0; i<data2; i++ )
      {
         std::cout << "run-id" << std::this_thread::get_id() << std::endl;
         std::this_thread::sleep_for (std::chrono::seconds(1));
      }
   };
};

Die Testmethode

/*
   Testet Thread3
*/
void test_Thread3()
{
   cout << "Thread3 example\n" << endl; //

   Thread3 thr;
   for(int i=0; i<3; i++ )
   {
      cout << "main-id" << std::this_thread::get_id()  << endl;
      std::this_thread::sleep_for (std::chrono::seconds(1));
   }
   // sayHello sollte zuerst kommen, dann run3
   thr.start(17, 7);
   for(int i=0; i<3; i++ )
   {
      cout << "main-id" << std::this_thread::get_id()  << endl;
      std::this_thread::sleep_for (std::chrono::seconds(1));
   }
   cout << "main-end" << endl;
}

Zwei Ausgaben


Beispiel 4

Der Thread in der Klasse erhält über start() einen äußeren std::thread, auf den er mit join() wartet

/*
   Bekommt über die start()-Methode einen std::thread von außen, auf den der
   interne Thread mit join() wartet
*/
class Thread4
{
private:
   std::thread th;
   std::thread other;
   bool running = false;

public:
   Thread4()
   {};

   virtual ~Thread4()
   {
      std::cout << "destruktor-id" << std::this_thread::get_id() << std::endl;
      this->th.join();
      std::cout << "destruktor-end-id" << std::this_thread::get_id() << std::endl;
   };

   void start(std::thread other)  // darf nur einmal passieren
   {
      this->other = std::move( other );  //
      if (running == false)
      {
         this->running = true;
         this->th = std::move( std::thread(&Thread4::run, this) );  //
         // swap geht hier nicht
      }
   };

   void run()
   {
      if( this->other.joinable() )
         this->other.join();

      for(int i=0; i<3; i++ )
      {
         std::cout << "run-id" << std::this_thread::get_id() << std::endl;
         std::this_thread::sleep_for (std::chrono::seconds(1));
      }
   };
};

Die Testmethode

/*
   Erst kommt main, dann wird der dem Threadobjekt über start() ein äußerer Thread übergeben,
   der sayHello() aufruft, da der innere Thread auf diesen wartet kommen due run-Ausgaben nach den
   hello-Ausgaben, währen main noch dazwischen Ausgaben macht
*/
void test_Thread4()
{
   cout << "Thread4 example\n" << endl; //

   Thread4 thr;
   for(int i=0; i<3; i++ )
   {
      cout << "main-id" << std::this_thread::get_id()  << endl;
      std::this_thread::sleep_for (std::chrono::seconds(1));
   }
   thr.start( std::thread(sayHello, 3) );
   // 3 mal hello, 3 mal run
   for(int i=0; i<4; i++ )
   {
      cout << "main-id" << std::this_thread::get_id()  << endl;
      std::this_thread::sleep_for (std::chrono::seconds(1));
   }

   cout << "main-end" << endl;
}

Zwei Ausgaben