Сейчас на форуме: tyns777, zds (+3 невидимых)

 eXeL@B —› Программирование —› Delphi, class, ошибка доступа к private из thread, помогите разобраться.
Посл.ответ Сообщение


Ранг: 450.3 (мудрец), 13thx
Активность: 0.20
Статус: Участник

Создано: 23 октября 2009 15:41
· Личное сообщение · #1

Помогите разобраться с проблемой. На примере это выглядит так:

Code:
  1. Type TStatistic=Class(TObject)
  2. private
  3. text: string;
  4. procedure ThreadFunc; stdcall;
  5. public
  6. procedure Test(sometext: string);
  7. end;
  8.  
  9. implementation
  10.  
  11. Procedure TStatistic.ThreadFunc; stdcall;
  12. begin
  13. showmessage('Это сообщение будет выдано.');
  14. showmessage(text); // Тут падает с ошибкой.
  15. end;
  16.  
  17. Procedure TStatistic.Test(sometext: string);
  18. Var hThread,ThID:dword;
  19. begin
  20. text:=sometext;
  21. CreateThread(nil,0,@TStatistic.ThreadFunc,nil,0,ThID);
  22. end;


После вызова Test падает в треде на доступе к text. Задача требует именно создание треда в котором будет вызвана процедура работающая со строкой text. Если из треда вызывать процедуру которая обращается к text, то падает на обращении в этой процедуре.




Ранг: 2014.5 (!!!!), 1278thx
Активность: 1.340.25
Статус: Модератор
retired

Создано: 23 октября 2009 17:11
· Личное сообщение · #2

Выложил бы ехе, сам поглядел под отладчиком. Есть подозрение, что члены класса вызываются через указатель на класс, которого у потока нет.




Ранг: 450.3 (мудрец), 13thx
Активность: 0.20
Статус: Участник

Создано: 23 октября 2009 18:18 · Поправил: ToBad
· Личное сообщение · #3

Немного модифицировал тут:
Code:
  1. Procedure TStatistic.ThreadFunc; stdcall;
  2. begin
  3. messagebox(0,'Это сообщение будет выдано.','Msg1',0);
  4. messagebox(0,PAnsiChar(text),'Msg2',0); // Тут падает с ошибкой.
  5. end;


Вызываю так:
Code:
  1. procedure TForm1.FormCreate(Sender: TObject);
  2.   var Statistic:TStatistic;
  3. begin
  4. Statistic:=TStatistic.create;
  5. Statistic.test('11111');
  6. Statistic.Free;
  7. end;


Archer пишет:
Есть подозрение, что члены класса вызываются через указатель на класс, которого у потока нет.


Да, буду смотреть под отладчиком. То, что пальцем в небо указывает, а не на строку и так понятно, вопрос в другом, как написать правильно, что бы обойти эту проблему.
Если в двух словах, то это всё делаю для обхода известной проблемы с InternetReadFile, которая не имеет таймаута и в случае отсутствия инета зависает довольно на долго.
По этому планирую вызывать это в треде и задавать таймаут через WaitForSingleObject.
Зачем полез создавать класс, сам уже не знаю, без него всё работало нормально...
Но раз уж начал, хотел бы побороть проблему...


2987_23.10.2009_CRACKLAB.rU.tgz - test.exe




Ранг: 990.2 (! ! !), 380thx
Активность: 0.680
Статус: Модератор
Author of DiE

Создано: 23 октября 2009 18:29 · Поправил: Модератор
· Личное сообщение · #4

ToBad пишет:
которая не имеет таймаута и в случае отсутствия инета зависает довольно на долго.


а как же:
Code:
  1.     timeout := 10 * 1000; // 10 sec
  2.     InternetSetOptionA(pInet,INTERNET_OPTION_CONNECT_TIMEOUT,@timeout,4);
  3.     InternetSetOptionA(pInet,INTERNET_OPTION_RECEIVE_TIMEOUT,@timeout,4);
  4.     InternetSetOptionA(pInet,INTERNET_OPTION_SEND_TIMEOUT,@timeout,4);


-----
[nice coder and reverser]





Ранг: 196.6 (ветеран), 11thx
Активность: 0.070.01
Статус: Участник

Создано: 23 октября 2009 19:17 · Поправил: [wl]
· Личное сообщение · #5

Если решать именно исходную проблему, то примерно так:
CreateThread кажется принимает одним из параметров произвольный указатель. Нужно при создании потока передать Self, а в функции потока привести этот указатель к TStatistic и уже через него обращаться к тексту.
и вообще, как-то криво поток создается, фунция потока должна иметь такой прототип (судя по MSDN):
DWORD WINAPI ThreadProc(
[in] LPVOID lpParameter
);

а не void proc(); как в твоем случае. Хотя... раз твоя функция не статическая, то один параметр у неё похоже будет (указатель на класс), куда на самом деле будет передан 0.



Ранг: 114.8 (ветеран), 41thx
Активность: 0.10
Статус: Участник

Создано: 23 октября 2009 19:24
· Личное сообщение · #6

Если text объявить как class var, то адрес строки нормально передается. Только вот у мну даже первый бокс не всегда вылазит



Ранг: 512.7 (!), 360thx
Активность: 0.270.03
Статус: Модератор

Создано: 23 октября 2009 20:45
· Личное сообщение · #7

ToBad
Раз работаете в Дельфиях, то желательно использовать их классы, а именно - TThread
Замечание - если в коде потока надо обращаться к ГУИ - используем Synchronize()

Отличный пример работы с TThread можно найти в подкаталоге Demos\Threads каталога, куда Вы установили Borland Delphi.

еще можно в нете почитать, например -
Первые шаги с TThread в Delphi
www.realcoding.net/article/view/158




Ранг: 450.3 (мудрец), 13thx
Активность: 0.20
Статус: Участник

Создано: 23 октября 2009 20:49
· Личное сообщение · #8

Hellspawn пишет:
а как же:
Code:
    timeout := 10 * 1000; // 10 sec
    InternetSetOptionA(pInet,INTERNET_OPTION_CONNECT_TIMEOUT,@timeout, 4);
    InternetSetOptionA(pInet,INTERNET_OPTION_RECEIVE_TIMEOUT,@timeout, 4);
    InternetSetOptionA(pInet,INTERNET_OPTION_SEND_TIMEOUT,@timeout,4);


К сожалению не работает так как должно. Причем не у меня одного...

[wl] пишет:
и вообще, как-то криво поток создается, фунция потока должна иметь такой прототип (судя по MSDN):
DWORD WINAPI ThreadProc(
[in] LPVOID lpParameter
);


Переделаю и попробую параметр передавать.

_ruzmaz_ пишет:
Если text объявить как class var, то адрес строки нормально передается. Только вот у мну даже первый бокс не всегда вылазит


Это как? Покажи пожалуйста примерчик.



Ранг: 114.8 (ветеран), 41thx
Активность: 0.10
Статус: Участник

Создано: 23 октября 2009 20:55 · Поправил: _ruzmaz_
· Личное сообщение · #9

ToBad пишет:
Это как?

Code:
  1. class var text: string;

вместо
Code:
  1. text: string;





Ранг: 450.3 (мудрец), 13thx
Активность: 0.20
Статус: Участник

Создано: 23 октября 2009 20:59
· Личное сообщение · #10

sendersu пишет:
Раз работаете в Дельфиях, то желательно использовать их классы, а именно - TThread
Замечание - если в коде потока надо обращаться к ГУИ - используем Synchronize()


В длл с этим классом возникали проблемы, вот тут об этом говорил. Как потом почитал, есть какие то проблемы с синхронизацией в случае с длл... А вообще мне тред нужен только для того, что бы поставить таймаут на выполнение функции работа которой может затянуться на долго. В случае использования TThread даже не знаю как это реализовать.




Ранг: 450.3 (мудрец), 13thx
Активность: 0.20
Статус: Участник

Создано: 23 октября 2009 21:05
· Личное сообщение · #11

_ruzmaz_ пишет:
class var text: string;


Там же где у меня сейчас написано? Дельфи говорит что ошибка: PROCEDURE or FUNCTION expected.
А вообще если пишу var text: string; после implementation, то работает, и извне недоступно. А зачем вообще это писать в привате если переменная только для внутреннего пользования?



Ранг: 114.8 (ветеран), 41thx
Активность: 0.10
Статус: Участник

Создано: 23 октября 2009 21:11
· Личное сообщение · #12

ToBad пишет:
Там же где у меня сейчас написано?

Да

ToBad пишет:
Дельфи говорит что ошибка: PROCEDURE or FUNCTION expected.

У меня ничего не говорит (Delphi 2006).




Ранг: 450.3 (мудрец), 13thx
Активность: 0.20
Статус: Участник

Создано: 23 октября 2009 21:31 · Поправил: ToBad
· Личное сообщение · #13

_ruzmaz_ пишет:
У меня ничего не говорит (Delphi 2006).


У меня Delphi 7.0...

[wl] пишет:
CreateThread кажется принимает одним из параметров произвольный указатель. Нужно при создании потока передать Self, а в функции потока привести этот указатель к TStatistic и уже через него обращаться к тексту.


Self передал, а как привести этот указатель к TStatistic что бы обращаться к text? И получается это нужно будет передавать дальше каждой вызываемой функции?
Дело в том, что такая конструкция тоже не работает правильно:
Code:
  1. Type TStatistic=Class(TObject)
  2. private
  3. text: string;
  4. procedure Problem;
  5. function ThreadFunc(x:dword):dword; stdcall;
  6. public
  7. procedure Test(sometext: string);
  8. end;
  9.  
  10. implementation
  11.  
  12. Procedure TStatistic.Problem;
  13. begin
  14. messagebox(0,'Это сообщение будет выдано.','Msg1',0);
  15. messagebox(0,PAnsiChar(text),'Msg2',0); // Проблемное место
  16. // Вылет с ошибкой пропал после правильного объявления ThreadFunc
  17. end;
  18.  
  19. Function TStatistic.ThreadFunc(x:dword):dword; stdcall;
  20. begin
  21. Problem; // Тут второй мессадж будет содержать пустую строку.
  22. // Работает не правильно при вызове из треда.
  23. end;
  24.  
  25. Procedure TStatistic.Test(sometext: string);
  26. Var hThread,ThID:dword;
  27. begin
  28. text:=sometext;
  29. Problem; // Тут будут два мессаджа и второй будет содержать строку из text
  30. // Работает правильно при вызове отсюда.
  31.  
  32. CreateThread(nil,0,@TStatistic.ThreadFunc,Self,0,ThID);
  33. end;




Ранг: 114.8 (ветеран), 41thx
Активность: 0.10
Статус: Участник

Создано: 23 октября 2009 22:25
· Личное сообщение · #14

ToBad пишет:
Дельфи говорит что ошибка: PROCEDURE or FUNCTION expected.

Да, посмотрел в Delphi 7.0 - не работает, видимо позже добавили.

ToBad пишет:
А вообще если пишу var text: string; после implementation, то работает, и извне недоступно.

И так можно, но я думал что тебе нужно было чтоб text не выходила за пределы класса.




Ранг: 196.6 (ветеран), 11thx
Активность: 0.070.01
Статус: Участник

Создано: 23 октября 2009 22:45
· Личное сообщение · #15

приведение из одного типа в другой примерно так:

Function TStatistic.ThreadFunc(x:dword):dword; stdcall;
begin
TStatictic(x).text
end;

Но работать это не будет мне кажется, так как ThreadFunc объявлена как нестатическая функция класса, то есть компилятор втихаря подставит лишний аргумент - указатель на Self:

Function TStatistic.ThreadFunc(Self: TStatistic, x:dword):dword; stdcall;

а нельзя никак эту функцию потока вынести из класса, чтобы не было параметра Self?



Ранг: 13.7 (новичок)
Активность: 0.010
Статус: Участник

Создано: 24 октября 2009 11:05
· Личное сообщение · #16

Во-первых, меняем
showmessage('Это сообщение будет выдано.');
showmessage(text); // Тут падает с ошибкой.

на

messagebox(0,'Это сообщение будет выдано.','q',0);
messagebox(0,pchar(text),'q',0); // Тут ничего не падает

затем меняем
CreateThread(nil,0,@TStatistic.ThreadFunc,nil,0,ThID);

на

CreateThread(nil,0,@TStatistic.ThreadFunc,self,0,ThID);

После этого всё работает. Как тут уже сказали, в ThreadFunc неявно передаётся параметр self, но он там, собственно, и ожидается ))




Ранг: 450.3 (мудрец), 13thx
Активность: 0.20
Статус: Участник

Создано: 24 октября 2009 12:32
· Личное сообщение · #17

Большое спасибо всем, кто отписался в теме, принял участие и помог!!!

Nowar пишет:
После этого всё работает.


Посмотри код на 2 поста выше твоего. Вчера всё это пробовал и не работало, но сегодня прочитав такую уверенность в твоём посте попробовал ещё раз и теперь работает... Понимаю, что звучит бредово...

Вот привожу полностью код который получился, может кому сгодится, нормального примера в инете не нашёл, лишь общие намёки, вывел процедурку PageToStr через которую получаю страничку в строку, конечно не нравится момент с буфером, нужно по умному сделать будет, но код писался под конкретную задачу, а в ней это допустимо.

Code:
  1. unit MyInet;
  2.  
  3. interface
  4.  
  5. Uses windows, classes, wininet;
  6.  
  7. Type TMyInetClass=Class(TObject)
  8. private
  9. Global, Url: string;
  10. hSession, hURL: HInternet;
  11. procedure worker;
  12. Function ThreadFunc(x:dword):dword; stdcall;
  13. public
  14. TimeOut:integer;
  15. Constructor Create;
  16. Destructor Destroy; override;
  17. function GetInetFileToStr(const fileURL: string):string;
  18. end;
  19.  
  20. function PageToStr(fileURL: string; timeout:integer):string;
  21.  
  22. implementation
  23.  
  24. const
  25.   BufferSize=1000000;
  26.  
  27. function PageToStr(fileURL: string; timeout:integer):string;
  28. var St:TMyInetClass;
  29. begin
  30. St:=TMyInetClass.Create;
  31. St.TimeOut:=Timeout;
  32. Result:=St.GetInetFileToStr(fileURL);
  33. St.Free;
  34. end;
  35.  
  36. constructor TMyInetClass.Create;
  37. begin
  38. inherited;
  39. TimeOut:=1000;
  40. end;
  41.  
  42. destructor TMyInetClass.Destroy;
  43. begin
  44. inherited;
  45. end;
  46.  
  47. procedure TMyInetClass.worker;
  48. Var
  49.   Buffer: array[1..BufferSize] of Byte;
  50.   BufferLen: DWORD;
  51.   s: string;
  52. begin
  53.   hSession:=InternetOpen('Mozilla/4.0 (compatible; MSIE 5.0; Windows 98)',INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0);
  54.   try
  55.     hURL:= InternetOpenURL(hSession,PChar(url),nil,0,0,0);
  56.     try
  57.         InternetReadFile(hURL, @Buffer, SizeOf(Buffer), BufferLen);
  58.         SetLength(s,BufferLen);
  59.         move(Buffer, s[1], BufferLen);
  60.         Global:=s;
  61.     finally
  62.       InternetCloseHandle(hURL)
  63.     end;
  64.   finally
  65.     InternetCloseHandle(hSession)
  66.   end;
  67. end;
  68.  
  69. Function TMyInetClass.ThreadFunc(x:dword):dword; stdcall;
  70. begin
  71. worker;
  72. end;
  73.  
  74. function TMyInetClass.GetInetFileToStr(const fileURL: string):string;
  75. Var hThread,ThID:dword;
  76. begin
  77. URL:=fileURL;
  78. hThread:=CreateThread(nil,0,@TMyInetClass.ThreadFunc,Self,0,ThID);
  79. if WaitForSingleObject(hThread,TimeOut)=WAIT_TIMEOUT then Global:='';
  80. CloseHandle(hThread);
  81. InternetCloseHandle(hURL);
  82. InternetCloseHandle(hSession);
  83. Result:=Global;
  84. end;
  85.  
  86. end.


Вот я одного не пойму, зачем это делать классом? Может проще и лучше простой юнит? На конкретно этом примере есть недостатки или преимущества? Может кто нибудь в двух словах объяснить?



Ранг: 512.7 (!), 360thx
Активность: 0.270.03
Статус: Модератор

Создано: 24 октября 2009 16:01
· Личное сообщение · #18

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

хочу услишать аргументы за не-классовый подход



Ранг: 309.8 (мудрец), 21thx
Активность: 0.170
Статус: Участник

Создано: 24 октября 2009 16:30
· Личное сообщение · #19

namespace может почти всё из поста #18.

-----
Shalom ebanats!




Ранг: 2.6 (гость)
Активность: 0=0
Статус: Участник

Создано: 26 октября 2009 16:14
· Личное сообщение · #20

если интересно, то данная проблема есть одно из проявлений того, что VCL НЕ thread safe.
а именно, showMessage в глубине себя создает форму, что НЕЛЬЗЯ делать из потока.
да, МОЖЕТ прокатить. но с вероятностью 50% - подвиснет,свалится и тд.

посему надобно практически всегда использовать synchronize, если есть обращение к VCL...

сорри,ежели баян.



Ранг: 17.6 (новичок)
Активность: 0.01=0.01
Статус: Участник

Создано: 30 октября 2009 19:30
· Личное сообщение · #21

В Delphi нужно использовать класс TTРread использование CreateThread некорректоно , подробности
у Рихтера


 eXeL@B —› Программирование —› Delphi, class, ошибка доступа к private из thread, помогите разобраться.
:: Ваш ответ
Жирный  Курсив  Подчеркнутый  Перечеркнутый  {mpf5}  Код  Вставить ссылку 
:s1: :s2: :s3: :s4: :s5: :s6: :s7: :s8: :s9: :s10: :s11: :s12: :s13: :s14: :s15: :s16:


Максимальный размер аттача: 500KB.
Ваш логин: german1505 » Выход » ЛС
   Для печати Для печати