Что если конструктор не успел завершиться, а я его delete?
От: Mazay Россия  
Дата: 24.05.04 10:03
Оценка:
Пусть есть отдельный поток, в котором в цикле создаются объекты. Конструкторы этих объектов выполняют операции, которые могут заблокировать поток (например accept). Через некоторое время основной поток решает прекратить работу потока создающего объекты. Что станет с объектом, для которого конструктор не успел завершить работу? Как его удалять из памяти? Чем череват такой подход?
ЗЫ
Я пока не говорю об обращению к "недосконструированному" объекту из другого потока.
Вот пример:
#include <stdio.h>
#include <iostream>
#include <list>
#include <windows.h>
using std::cout;
using std::endl;
using std::list;

int ui = 0;

class MyClass
{
 public:
 int i;
 
 MyClass()
 {
  i = ui++;
  cout<<i<<" constr begin"<<endl;
  Sleep(3000);
  cout<<i<<" constr end"<<endl;  
 }
 ~MyClass()
 {
  cout<<i<<" destr"<<endl;
 }
};

list<MyClass*> l;

DWORD WINAPI thread(void *p)
{
 MyClass *pMC;
 while(1)
 {
  pMC = new MyClass();
  l.push_back(pMC); 
 }
 return 0;
}

int main()
{
 HANDLE h;
 h = CreateThread(NULL,0,thread,NULL,0,NULL);
 getchar();
 TerminateThread(h,0);
 while ( !(l.empty()) )
 {
  cout<< (*(l.begin()))->i<<"    ";
  delete (*(l.begin()));
  l.pop_front();
 }
 return 0;
}

Результат работы:

C:\prg\tst1>1.exe
0 constr begin
0 constr end
1 constr begin
1 constr end
2 constr begin

0 0 destr
1 1 destr

C:\prg\tst1>

Упс. А где же 3-й деструктор?...
PPS
Если есть комментарии по коду не касающиеся сабжа, прошу высказывать их. Учусь
Главное гармония ...
Re: Что если конструктор не успел завершиться, а я его delet
От: Андрей Россия  
Дата: 24.05.04 10:14
Оценка:
Здравствуйте, Mazay, Вы писали:

skip

Ну ты и изверг

Лучше создать событие, в нужный момент уго установить в основном потоке и ждать, когда завершится дополнительный.
А в дополнительном потоке в цикле, где создаешь объекты, проверять это событие и если оно установлено — завершить цикл.
Re[2]: Что если конструктор не успел завершиться, а я его de
От: Mazay Россия  
Дата: 24.05.04 10:26
Оценка:
Здравствуйте, Андрей, Вы писали:

А>Здравствуйте, Mazay, Вы писали:


А>skip


А>Ну ты и изверг


А>Лучше создать событие, в нужный момент уго установить в основном потоке и ждать, когда завершится дополнительный.

А>А в дополнительном потоке в цикле, где создаешь объекты, проверять это событие и если оно установлено — завершить цикл.

Ну а если в конструкторе не Sleep(3000) а скажем accept() блокирующего сокета, а в основном потоке вдруг становится очевидно что ждать коннекта можно будет до конца света. Тогда что?
Вообще-то можно просто добраться до сокета и закрыть его, ну а если сокет — член класса?
А в принципе, что это за "недоделка" такая получается и что с ним делать можно?
Главное гармония ...
Re: Что если конструктор не успел завершиться, а я его delet
От: Вадим Никулин Россия Здесь
Дата: 24.05.04 10:30
Оценка:
Здравствуйте, Mazay, Вы писали:

M>
M>int main()
M>{
M> HANDLE h;
M> h = CreateThread(NULL,0,thread,NULL,0,NULL);
M> getchar();
M> TerminateThread(h,0);
M> while ( !(l.empty()) )
M> {
M>  cout<< (*(l.begin()))->i<<"    ";
M>  delete (*(l.begin()));
M>  l.pop_front();
M> }
M> return 0;
M>}
M>


А вот господин Рихтер утверждает, что в C++ нехорошо использовать CreateThread/TerminateThread. Лучше — _beginthreadex и чтобы поток сам завершился. Управлять этим можно, например, через событие. Тогда и проблем таких не будет.
Re: Что если конструктор не успел завершиться, а я его delet
От: Mr. None Россия http://mrnone.blogspot.com
Дата: 24.05.04 10:35
Оценка:
Здравствуйте, Mazay, Вы писали:

Где, где

M>
// ...
M>int main()
M>{
M> HANDLE h;
M> h = CreateThread(NULL,0,thread,NULL,0,NULL);
M> getchar();
M> TerminateThread(h,0);  // <- ВОТ ГДЕ!
M> while ( !(l.empty()) )
M> {
M>  cout<< (*(l.begin()))->i<<"    ";
M>  delete (*(l.begin()));
M>  l.pop_front();
M> }
M> return 0;
M>}
M>


Ты замочил поток по жёсткому, даже конструктор не успел отработать, а уж операция l.push_back(pMC); даже не началась. Об этом же свидетельствует и отладочный вывод. Попробуй вставить ещё вот такую строку: cout << l.size(), она тебе тоже выведет не 3, а 2.
Компьютер сделает всё, что вы ему скажете, но это может сильно отличаться от того, что вы имели в виду.
Re[2]: Что если конструктор не успел завершиться, а я его de
От: Mr. None Россия http://mrnone.blogspot.com
Дата: 24.05.04 10:37
Оценка:
Здравствуйте, Вадим Никулин, Вы писали:

ВН>Здравствуйте, Mazay, Вы писали:


M>>
M>>int main()
M>>{
M>> HANDLE h;
M>> h = CreateThread(NULL,0,thread,NULL,0,NULL);
M>> getchar();
M>> TerminateThread(h,0);
M>> while ( !(l.empty()) )
M>> {
M>>  cout<< (*(l.begin()))->i<<"    ";
M>>  delete (*(l.begin()));
M>>  l.pop_front();
M>> }
M>> return 0;
M>>}
M>>


ВН>А вот господин Рихтер утверждает, что в C++ нехорошо использовать CreateThread/TerminateThread. Лучше — _beginthreadex и чтобы поток сам завершился. Управлять этим можно, например, через событие. Тогда и проблем таких не будет.


Это-то ессно, но в данном случае даже это не поможет. Я уже написал — поток был уничтожен ещё до того, как отработал последний l.push_back(pMC); поэтому последний объект не был помещён в список и при уничтожении delete для него естественно не был вызван...
Компьютер сделает всё, что вы ему скажете, но это может сильно отличаться от того, что вы имели в виду.
Re[2]: Что если конструктор не успел завершиться, а я его de
От: Mazay Россия  
Дата: 24.05.04 10:45
Оценка: +1
Здравствуйте, Вадим Никулин, Вы писали:

ВН>А вот господин Рихтер утверждает, что в C++ нехорошо использовать CreateThread/TerminateThread. Лучше — _beginthreadex и чтобы поток сам завершился. Управлять этим можно, например, через событие. Тогда и проблем таких не будет.


Хммм. Интересно. А я то как раз и отказался от _beginthreadex() из-за того что такой поток нельзя грохнуть вручную.
То есть, если ты не можешь обойтись без TerminateThread(), то ты лох, и нужно менять идеологию программы? Правильно?
Главное гармония ...
Re[3]: Что если конструктор не успел завершиться, а я его de
От: Mr. None Россия http://mrnone.blogspot.com
Дата: 24.05.04 10:50
Оценка: +1
Здравствуйте, Mazay, Вы писали:

M>Здравствуйте, Вадим Никулин, Вы писали:


ВН>>А вот господин Рихтер утверждает, что в C++ нехорошо использовать CreateThread/TerminateThread. Лучше — _beginthreadex и чтобы поток сам завершился. Управлять этим можно, например, через событие. Тогда и проблем таких не будет.


M>Хммм. Интересно. А я то как раз и отказался от _beginthreadex() из-за того что такой поток нельзя грохнуть вручную.

M>То есть, если ты не можешь обойтись без TerminateThread(), то ты лох, и нужно менять идеологию программы? Правильно?

Ты сам это сказал
Компьютер сделает всё, что вы ему скажете, но это может сильно отличаться от того, что вы имели в виду.
Re[2]: Что если конструктор не успел завершиться, а я его de
От: Mazay Россия  
Дата: 24.05.04 10:52
Оценка:
Здравствуйте, Mr. None, Вы писали:

MN>Где, где


MN>Ты замочил поток по жёсткому, даже конструктор не успел отработать, а уж операция l.push_back(pMC); даже не началась. Об этом же свидетельствует и отладочный вывод. Попробуй вставить ещё вот такую строку: cout << l.size(), она тебе тоже выведет не 3, а 2.

Не, я понимаю что происходит. Мне интересно чем это черевато и, если черевато чем-то совсем нехорошим, что полагается делать если конструтор может заблокировать поток на неопределённое время?
Главное гармония ...
Re[4]: Что если конструктор не успел завершиться, а я его de
От: Mr. None Россия http://mrnone.blogspot.com
Дата: 24.05.04 11:10
Оценка: 2 (1)
Здравствуйте, Mr. None, Вы писали:

MN>Здравствуйте, Mazay, Вы писали:


M>>Здравствуйте, Вадим Никулин, Вы писали:


ВН>>>А вот господин Рихтер утверждает, что в C++ нехорошо использовать CreateThread/TerminateThread. Лучше — _beginthreadex и чтобы поток сам завершился. Управлять этим можно, например, через событие. Тогда и проблем таких не будет.


M>>Хммм. Интересно. А я то как раз и отказался от _beginthreadex() из-за того что такой поток нельзя грохнуть вручную.

M>>То есть, если ты не можешь обойтись без TerminateThread(), то ты лох, и нужно менять идеологию программы? Правильно?

MN>Ты сам это сказал


А если серьёздно, то самый лучший вариант завершения потока — нормальный выход из главной функции потока. Могу предложить следующую схему:


void CALLBACK APCStub(ULONG_PTR){};

bool isWorking = true;

DWORD MyThread(void *val)
{
    while(isWorking)
    {
        DWORD res = ::WaitForSingleObjectEx(h, 5000, TRUE); // <- Обрати внимание, ожидаем в тревожном состоянии!
                                                          // Это касается всех функций ожидания
        switch(res)
        {
            case WAIT_OBJECT_0:
                 // Всё ок
            break;

            case WAIT_IO_COMPLETION:
                 // Ничего не делаем - это признак того, что кто-то хочет завершить поток. См. ниже
            break;

            defualt:
                 // ошибка
            break;
        }
    }
}


main()
{
    HANDLE my_thread = reinterpret_cast<HANDLE>(_beginthreadex(0, 0, MyThread, 0, 0, 0));
    ::Sleep(10000);
    isWorking = false;    // Выставляем признак завершения потока
    ::QueueUserAPC(APCStub, my_thread, 0); // Посылаем в поток фиктивный асинхронный вызов, это приведёт к тому,
                                           // что любая функция тревожного ожидания в потоке немедленно
                                           // завершиться с кодом WAIT_IO_COMPLETION. Тем самым мы можем управлять
                                           // функциями ожидания внутри потока (в том числе и SleepEx)
    if(::WaitForSingleObject(my_thread, 10000) == WAIT_TIMEOUT)
    {
        // Сюда мы можем попасть только если поток не завершмился в течении 10 секунд...
        // вот теперь мы с ним не церемонимся
        ::TerminateThread(my_thread, 1);
    }
}
Компьютер сделает всё, что вы ему скажете, но это может сильно отличаться от того, что вы имели в виду.
Re[3]: Что если конструктор не успел завершиться, а я его de
От: Mr. None Россия http://mrnone.blogspot.com
Дата: 24.05.04 11:21
Оценка:
Здравствуйте, Mazay, Вы писали:

M>Здравствуйте, Mr. None, Вы писали:


MN>>Где, где


MN>>Ты замочил поток по жёсткому, даже конструктор не успел отработать, а уж операция l.push_back(pMC); даже не началась. Об этом же свидетельствует и отладочный вывод. Попробуй вставить ещё вот такую строку: cout << l.size(), она тебе тоже выведет не 3, а 2.

M>Не, я понимаю что происходит. Мне интересно чем это черевато и, если черевато чем-то совсем нехорошим, что полагается делать если конструтор может заблокировать поток на неопределённое время?

Выше уже ответил... Всё то, что я описал можно завернуть в очень удобный класс.
Как альтернатива для сигнализации завершения можно использовать сообщение и внутри потока использовать только функции вида WaitForMultipleObjects. Мне этот вариант не нравится по 3 причинам:
1) число WaiteEx-функций больше, чем WaitMultiple-функций (взять тот же SleepEx);
2) интерфейс WaiteEx-функций проще, чем WaitMultiple-функций, поэтому я предпочитаю по возможности пользоваться 1-ым вариантом;
3) максимальное число объектов ожидания, допустимых к использованию в WaitForMultipleObjects равно 63-ём.. один всегда гарантировано занят данным событием, если вы захотите задействовать все 63 под свои нужды, то вы жестоко обламаетесь.
Компьютер сделает всё, что вы ему скажете, но это может сильно отличаться от того, что вы имели в виду.
Re[5]: Что если конструктор не успел завершиться, а я его de
От: Mazay Россия  
Дата: 24.05.04 11:26
Оценка:
Здравствуйте, Mr. None,

Интересно. Дома попробую применить.
Главное гармония ...
Re[3]: Что если конструктор не успел завершиться, а я его de
От: Olegator  
Дата: 24.05.04 18:18
Оценка:
Здравствуйте, Mazay, Вы писали:

M>Хммм. Интересно. А я то как раз и отказался от _beginthreadex() из-за того что такой поток нельзя грохнуть вручную.


A thread created with _beginthreadex is terminated by a call to _endthreadex


С уважением,
Olegator
... << RSDN@Home 1.1.3 beta 1 >>
Re[2]: Что если конструктор не успел завершиться, а я его de
От: Аноним  
Дата: 26.05.04 07:14
Оценка:
ВН>А вот господин Рихтер утверждает, что в C++ нехорошо использовать CreateThread/TerminateThread. Лучше — _beginthreadexи чтобы поток сам завершился. Управлять этим можно, например, через событие. Тогда и проблем таких не будет.
Перечитайте еще раз Рихтера и поймете в каком случае желательно использовать _beginthreadex().
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.