Тема: Багатопотоковість, бібліотека ZThread та метод interrupt().
Всім привіт!
В ефірі вечірній випуск передачі "Незрозуміла поведінка" чи, кажучи іншими словами, "Якась нєвєдома ф...ня, або де я налажав". В гостях у нас сьогодні бібліотека багатопотоковості ZThread, з цікавою, майже трагічною розповіддю як її ніби-то і розуміють, але щось десь не так... Отже, до справи.
Потік типу Thread може викликати метод interrupt, який встановлює мітку переривання потоку, якось так... Тобто цю мітку можна використовувати як умову виходу з циклу метода run():
void InterruptTest::run() {
while( !ZThread::Thread::interrupted() ) { // перевірка перервання роботи потоку
...
}
Але є одне але. Якщо заблокувати потік, який знаходиться в стані interrupted, але ще не завершився, то буде піднятий вийняток Interrupted_Exception, який завершить роботу методу run(). Тобто є дві "можливості" завершення потоку за допомогою методу interrupt - перевірка умови циклу та підняття вийнятку.
Якщо розкоментувати стрічку ZThread::Thread::sleep( 100 ), яка блокує потік на певний час, то під час блокування потоку підніметься вийняток Interrupted_Exception і результат буде таким - кілька стрічок "New iteration", а потім стрічка "Exiting via Interrupted_Exception" із блоку catch.
Якщо ж цю стрічку закоментувати то результат повинен бути - кілька стрічок "New iteration", а потім стрічка "Exiting via while() test".
Так от, проблема полягає в тому що в другому варіанті цикл не завершується, а виконується нескінченно. І я не можу зрозуміти чому.
#include <iostream>
#include <zthread/Thread.h>
using std::cout;
using std::endl;
const double PI = 3.1415965358979323846;
const double E = 2.718281824590452354;
class InterruptTest : public ZThread::Runnable {
private:
volatile double _d;
public:
InterruptTest() : _d( 0.0 ) {};
void run();
};
void InterruptTest::run() {
try {
while( !ZThread::Thread::interrupted() ) {
cout << "New iteration" << endl;
// ZThread::Thread::sleep( 100 );
for( int i = 0; i < 10000000; ++i )
_d = _d + ( PI + E ) / static_cast< double >( i );
}
cout << "Exiting via while() test" << endl;
}
catch( ZThread::Interrupted_Exception& e ) {
cout << "Exiting via Interrupted_Exception" << endl;
}
}
int main( int argc, char **argv ) {
int sleep = 1000;
if( argc > 1 )
sleep = std::atoi( argv[ 1 ] );
try {
// створюємо потік thread
ZThread::Thread thread( new InterruptTest );
// робимо затримку основного потоку
ZThread::Thread::sleep( sleep );
// "перериваємо" потік thread
thread.interrupt();
}
catch( ZThread::Synchronization_Exception& e ) {
cout << e.what() << endl;
}
return 0;
}
Є один важливий нюанс - метод interrupted() скидає статус переривання потоку. Тобто для кожного виклику interrupt() перевірка статусу interrupted() спрацьовує тільки один раз. "Це гарантує що бібліотека не оповістить про переривання двічі.". Можливо, проблема криється саме в цьому, але поки що це мені нічого не дало...