Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Системное программирование и WinAPI > Потокозащищенность объектов


Автор: orlangur86 17.7.2008, 11:01
Доброго времени суток всем форумчанам!
Может быть данная тема уже поднималась и на форуме уже есть ответы на мои вопросы - что ж, тогда прошу простить, плохо искал (искал - это факт, но может не по тем ключевым словам)
и был бы благодарен за ссылку на соответствующую ветку форума.

Собственно проблема. Существует класс (пусть будет класс А) для работы в сети (ну скажем фтп-клиент), есть другой класс ( класс Б), который использует вышеуказаный сетевой компонент в своих целях.
есть необходимость работы класса Б паралельно. Т.е. необходимо использовать потоки. Потоки вначале были реализованы на базе класса QThread (Qt lib) в виде: класс Б наследует от QThread и реализует метод обработки, за тем на базе того же QThread но это уже был отдельный класс, которому передавался указатель на Б, и тот уже при запуске вызывал обработку Б.
Так же пробовал реализовать на базе Thread из библиотеки POCO (portable components). Создание объектов потока и класса Б происходит следующим образом.
в основной программе: 
1) обрабатывается командная строка
2) определяется число необходимых потоков
3) в цикле происходит:
   - создание объекта Б (динамически через new)
        - в конструкторе Б происходит создание объекта А (так же через new) и указатель на А сохраняется как private член класса (не static)
  - прочие настройки (не существенные для данного вопроса)
  - создание объекта потока с передачей в конструктор в качестве параметра, указатель на Б
  - запуск потока
4) join для всех потоков.

Предполагается, что каждый поток пользуется своей копией Б, и своей копией А. Проблема возникает в том, что судя по логам, которые записываются при обращении к методам объекта класса А, этот объект вызывается как из родного потока, для которого он был создан, так и из чужих потоков, что недопустимо и в результате получается полный бред без всякой логики работы.

Теперь вопрос. Как такое возможно, чтоб потоки вдруг получали доступ к памяти, по большому счету, указатель на которую они вообще знать не должны?
Могу предположить, что при переключении контекстов потоков значения членов класса не меняются чтоль? так это как-то глупо... или я чего-то не знаю...

помогите советом, кто может! smile
заранее благодарю

Автор: Lycifer 17.7.2008, 11:20
ТЫ используешь одну и туже память(у тебя должен быть кон7структор копирывания)

Автор: EnergoHokum 17.7.2008, 11:37
Опиши подробнее, как ты формируешь логи. Кажется мне, всё дело в них.

Автор: orlangur86 17.7.2008, 11:56
Lycifer,  это как же я использую одну и ту же память если в тестовом отладочном варианте для 3-х потоков я создаю 3 объекта класса Б (как я уже писал при помощи new) и в конструкторе каждого объекта Б вызывается конструктор А (так же при помощи new)


EnergoHokum, логи формируются следующим образом:
в отладочном варианте и конструктор класса А и конструктор класса Б принимает доп параметр - число (ну в частности просто порядковый номер потока от 0 до 2 в отладочном случае)
в конструкторе А создается файл (при помощи std::fstream класса) с именем some_name_<число которое передано в конструктор>.log
при выполнении какого-либо метода А в лог записывается следующая информация: <число для отладки> <this приведеное к unsigned> <GetCurrentThreadId()> ну инфа по методу всякая

и вот что число для отладки, что this имеют одно и то же значение (ну логично в принципе), но вот ID потока разный. причем такой микс во всех 3-х потоках, во всех 3-х лог-файлах, которые создаются... я в замешательстве

Автор: Lazin 17.7.2008, 12:14
Цитата(orlangur86 @  17.7.2008,  11:01 Найти цитируемый пост)
Предполагается, что каждый поток пользуется своей копией Б, и своей копией А. Проблема возникает в том, что судя по логам, которые записываются при обращении к методам объекта класса А, этот объект вызывается как из родного потока, для которого он был создан, так и из чужих потоков, что недопустимо и в результате получается полный бред без всякой логики работы.

покажи конструктор копирования класса В

Добавлено через 1 минуту и 20 секунд
и конструктор класса В, и как создаешь и передаешь данные в потоки

Автор: orlangur86 17.7.2008, 12:42
конструкторы копирования для А и для Б нигде не исопльзуются. даже в неявном виде. в одном месте происходит создание Б при помощи new, и в одном месте происходит удаление Б
та же фигня и для А

Класс Б объявлен примерно так:

Код

class classB{
 private:
  classA *obj;
 public:
classB(int debug_val=0);
virtual ~classB();
 ....
void process(); //метод с полезной нагрузкой
};


Вот его конструктор и деструктор

Код

classB::classB(int debug_val)
{
  obj=new classA(debug_val);
}

classB::~classB()
{
 delete obj;
}


Создание потоков и передача в них данных:

в последней переделке/реализации сделано через POCO (portable Components) и их реализацию потоков.

т.е.
Код

class ThreadItem: public Runnable
{
 private:
 classA *member;
 public:
 ThreadItem(classA *a)
 {
   member=a;
 }
 virtual ~ThreadItem()
 {
  delete member;
 }
 virtual void run()
 {
   member->process();
 }
}

в main'е все происходит примерно следующим образом

Код

...
int count_of_threads=3; //ну для отладки просто 3 потока
vector<ThreadItem *> runs;
vector<Thread *> threads; //Thread - класс из POCO

for(int i=0;i<count_of_threads;++i)
{
  classA *ca=new classA(i); // тут передача того самого отладочного числа, номера потока
  ThreadItem *ti=new ThreadItem(ca);
  runs.push_back(ti);
  Thread *t=new Thread();
  threads.push_back(t);
 t->start(ti);
}

for(int i=0;i<count_of_threads;++i)
{
 threads[i]->join();
 delete threads[i];
 delete runs[i];
}
...



примерно так.

Автор: Lazin 17.7.2008, 12:53
похоже на то, что ты неправильно пишешь в лог... экземпляры В, при такой реализации будут разными...

Добавлено @ 12:59
кстати объекты у тебя создаются в одном потоке, но методы run должны вызываться из разных потоков...
если тебе нужно, что-бы объекты создавались в разных потоках, добавь код по созданию объекта в код самого потока, пускай поток сам управляет своими данными...

Код

class ThreadItem: public Runnable
{
 int ctor_data;
 boost::shared_ptr<classA> member;
 public:
 ThreadItem(int data) : Runable(), ctor_data (data), member()
 {
 }

 virtual ~ThreadItem()
 {
 }

 virtual void run()
 {
   //создаем объект принадлежащий потоку
   member = boost::shared_ptr<classA>( new classA(ctor_data) );
   member->process();
 }
};

Автор: orlangur86 17.7.2008, 13:01
хз...

Код

using namespace std;
classB
{
 private:
  int debug_val;
  fstream log;
  classB(int dv)  
 {
    char buf[5];
    string logname="classb_log_";
    logname+=itoa(dv,buf,10);
   logname+=".log";
    log.open(logname,ios::out);
   debug_val=dv;
  }
 virtual ~classB()
 {
   log.flush();
   log.close();
 }
 
void  method1()
 {
   log<<debug_val<<" "<<((unsigned)this)<<" "<<GetCurrentThreadId()<<" some other method1 info\r\n";
 }
void  method2()
 {
   log<<debug_val<<" "<<((unsigned)this)<<" "<<GetCurrentThreadId()<<" some other method2 info\r\n";
 }
};


ну и как я уже писал: GetCurrentThreadId() на этом месте встречаются 3 разных значения в разных строках лога

Автор: Lazin 17.7.2008, 13:02
в main теперь все проще:

Код

...
for(int i=0;i<count_of_threads;++i)
{
  ThreadItem *ti=new ThreadItem(i);
  runs.push_back(ti);
}
...


Добавлено через 2 минуты и 5 секунд
Цитата(orlangur86 @  17.7.2008,  13:01 Найти цитируемый пост)
ну и как я уже писал: GetCurrentThreadId() на этом месте встречаются 3 разных значения в разных строках лога 

ты уверен что у тебя вызывается метод одного объекта из разных потоков, а не один и тот-же метод разных объектов из разных потоков

Автор: orlangur86 17.7.2008, 13:14
Цитата(Lazin @ 17.7.2008,  13:02)
Цитата(orlangur86 @  17.7.2008,  13:01 Найти цитируемый пост)
ну и как я уже писал: GetCurrentThreadId() на этом месте встречаются 3 разных значения в разных строках лога 

ты уверен что у тебя вызывается метод одного объекта из разных потоков, а не один и тот-же метод разных объектов из разных потоков

ну да... дело в том, что отладочное число и адрес this для одного лог-файла - одни  и те же, а вот ИД потоков разные

на счет более простого кода в main - да, наверное так действительно проще, но и в моем варианте ничего противозаконного нету (это я к тому, что и там должны пахать нормально потоки... но не пашут...)

Автор: orlangur86 18.7.2008, 10:29
Спасибо всем, кто откликнулся!
Проблема как всегда в драйвере прямых рук ))
Один из ресурсов, который передавался классу Б в момент его инициализации, не был ни продублирован для каждого потока отдельно, и не был потоко-безопасным

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)