aguest2007
Гость
|
|
« : 19-11-2008 15:34 » |
|
Пишу программу на Borland C++ Builder 6 Создаю два потока, которые запускаются/останавливаются через CheckBox, написал, работает, пришел к другу, запустил у него на компе и появилось проблема: при запуске одного потока всё нормально, а вот когда запускаю одновременно с ним второй, - главное окно намертво виснет, причем на обоих его компах(Windows XP). У меня тоже XP и та же программа работает без проблем.Не пойму, в чем причина?
|
|
|
Записан
|
|
|
|
Dimka
Деятель
Команда клуба
Offline
Пол:
|
|
« Ответ #1 : 19-11-2008 16:16 » |
|
Очевидно, что в программе, которую мы тут не видели
|
|
|
Записан
|
Программировать - значит понимать (К. Нюгард) Невывернутое лучше, чем вправленное (М. Аврелий) Многие готовы скорее умереть, чем подумать (Б. Рассел)
|
|
|
aguest2007
Гость
|
|
« Ответ #2 : 19-11-2008 17:07 » |
|
Вот код: Unit1.cpp #include <vcl.h> #pragma hdrstop
#include "Unit1.h" #include "Unit2.h" #include "Unit3.h" //--------------------------------------------------------------------------- #pragma package(smart_init) #pragma resource "*.dfm" TForm1 *Form1; MyThread *th1; MyThread2 *th2; //--------------------------------------------------------------------------- __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { th1=new MyThread(true); th2=new MyThread2(true); } //---------------------------------------------------------------------------
void __fastcall TForm1::FormCreate(TObject *Sender) { th1->Priority=tpLowest; th2->Priority=tpLowest; } //---------------------------------------------------------------------------
void __fastcall TForm1::CheckBox1Click(TObject *Sender) { if(Form1->CheckBox1->Checked) th1->Resume(); else th1->Suspend(); } //---------------------------------------------------------------------------
void __fastcall TForm1::CheckBox2Click(TObject *Sender) { if(Form1->CheckBox2->Checked) th2->Resume(); else th2->Suspend(); } //---------------------------------------------------------------------------
void __fastcall TForm1::FormPaint(TObject *Sender) { Form1->Canvas->MoveTo(0,180); Form1->Canvas->LineTo(800,180); Form1->Canvas->MoveTo(400,0); Form1->Canvas->LineTo(400,330); } //---------------------------------------------------------------------------
Unit2.cpp #include <vcl.h> #include <math.h> #pragma hdrstop
#include "Unit2.h" #include "Unit1.h" #pragma package(smart_init)
#define MY_X_COORD(v) (xz+(v)) #define MY_Y_COORD(v) (yz-(v)) #define Y_COORD(v) (tan(float(v)/40.0)*30) int sx=-400; int mx=400; int x=sx; int y; int xz=400; int yz=180;
__fastcall MyThread::MyThread(bool CreateSuspended) : TThread(CreateSuspended) { } //--------------------------------------------------------------------------- void __fastcall MyThread::Execute() { while(1) Synchronize(this->UpdateCaption); } //--------------------------------------------------------------------------- void __fastcall MyThread::UpdateCaption() { if(!(MY_Y_COORD(Y_COORD(x))<0||MY_Y_COORD(Y_COORD(x))>330)) { Form1->Canvas->MoveTo(MY_X_COORD(x),MY_Y_COORD(Y_COORD(x))); Form1->Canvas->LineTo(MY_X_COORD(x+1),MY_Y_COORD(Y_COORD(x+1))); } if(!(++x<mx)) { x=sx; if(Form1->Canvas->Pen->Color==clRed) Form1->Canvas->Pen->Color=clBlue; else Form1->Canvas->Pen->Color=clRed; } }
Unit3.cpp #include <vcl.h> #include <math.h> #pragma hdrstop
#include "Unit3.h" #include "Unit1.h" #pragma package(smart_init)
#define MY_X_COORD(v) (xz+(v)) #define MY_Y_COORD(v) (yz-(v)) #define Y_COORD(v) (((v)*(v))/400) int sx=-400; int mx=400; int x=sx; int y; int xz=400; int yz=180;
__fastcall MyThread2::MyThread2(bool CreateSuspended) : TThread(CreateSuspended) { } //--------------------------------------------------------------------------- void __fastcall MyThread2::Execute() { while(1) { Synchronize(this->UpdateCaption); } } //--------------------------------------------------------------------------- void __fastcall MyThread2::UpdateCaption() { if(!(MY_Y_COORD(Y_COORD(x))<0||MY_Y_COORD(Y_COORD(x))>330)) { Form1->Canvas->MoveTo(MY_X_COORD(x),MY_Y_COORD(Y_COORD(x))); Form1->Canvas->LineTo(MY_X_COORD(x+1),MY_Y_COORD(Y_COORD(x+1))); } if(!(++x<mx)) x=sx; }
|
|
|
Записан
|
|
|
|
Dimka
Деятель
Команда клуба
Offline
Пол:
|
|
« Ответ #3 : 19-11-2008 18:57 » |
|
А что написано про Canvas? Он реализован как thread-safe? Т.е., допустим, рисует он себе линию, и в середине его обрывают и начинают требовать нарисовать другую линию - он может обидеться. Особенно если он рисует линию, используя какие-нибудь временные переменные уровня объекта.
Что касается разницы, то может быть ощутима разница между реально многопроцессорной системой и псевдопараллелизмом на одном физическом процессоре. В первом случае неаккуратности при параллельном программировании вылезают гораздо чаще.
|
|
|
Записан
|
Программировать - значит понимать (К. Нюгард) Невывернутое лучше, чем вправленное (М. Аврелий) Многие готовы скорее умереть, чем подумать (Б. Рассел)
|
|
|
aguest2007
Гость
|
|
« Ответ #4 : 19-11-2008 19:34 » |
|
Насчет Canvas вы совершенно правы, надо бы добавить, если я не ошибаюсь, ...->Canvas->Lock() и ...->Canvas->Unlock(). Однако так же себя повела и вторая программа, которая Canvas не использует, а работает с компонентами TListView.Пробовал комментировать в Unit3 строки, изменяющие ListView2, чтобы второй поток не вносил изменений в него - не помогло.Подозреваю что даже если оба потока будут работать вхолостую, дело не поправится(.Тут может в настройках билдера или конфигурациях систем проблема.На моей системе всё работает отлично. Вот код второй программы Первый поток отслеживает появление в ListView1 слов не кодированных,кодирует и пишет в ListView2 Второй поток отслеживает появление в ListView2 слов кодированных,раскодирует и перезаписывает Определения в Unit1.h для всех модулей ... bool coded=true; bool uncoded=false; #define Coded ((TObject*)&coded) #define Uncoded ((TObject*)&uncoded) ...
Unit1 #include <vcl.h> #pragma hdrstop
#include "Unit1.h" #include "Unit2.h" #include "Unit3.h" //--------------------------------------------------------------------------- #pragma package(smart_init) #pragma resource "*.dfm" TForm1 *Form1; MyThread *th1; MyThread2 *th2; //--------------------------------------------------------------------------- __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { th1=new MyThread(true); th2=new MyThread2(true); } //---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender) { Form1->ListView1->AddItem(Form1->Edit1->Text,Uncoded); } //--------------------------------------------------------------------------- void __fastcall TForm1::FormCreate(TObject *Sender) { th1->Priority=tpLowest; th2->Priority=tpLowest; } //---------------------------------------------------------------------------
void __fastcall TForm1::CheckBox1Click(TObject *Sender) { if(Form1->CheckBox1->Checked) th1->Resume(); else th1->Suspend(); } //---------------------------------------------------------------------------
void __fastcall TForm1::CheckBox2Click(TObject *Sender) { if(Form1->CheckBox2->Checked) th2->Resume(); else th2->Suspend(); } //---------------------------------------------------------------------------
Unit2 #include <vcl.h> #pragma hdrstop
#include "Unit2.h" #include "Unit1.h" #pragma package(smart_init)
__fastcall MyThread::MyThread(bool CreateSuspended) : TThread(CreateSuspended) { } //--------------------------------------------------------------------------- void __fastcall MyThread::Execute() { while(1) Synchronize(this->UpdateCaption); } //--------------------------------------------------------------------------- void __fastcall MyThread::UpdateCaption() { int c=Form1->ListView1->Items->Count; for(int i=0;i<c;i++) if(*(bool *)(Form1->ListView1->Items->Item[i]->Data)==false) { Form1->ListView1->Items->Item[i]->Data=Coded; AnsiString s; s=Form1->ListView1->Items->Item[i]->Caption; for(int k=1;k<=s.Length();k++) s[k]=s[k]^77; Form1->ListView2->AddItem(s,Coded); } }
Unit3 #include <vcl.h> #pragma hdrstop
#include "Unit3.h" #include "Unit1.h" #pragma package(smart_init)
__fastcall MyThread2::MyThread2(bool CreateSuspended) : TThread(CreateSuspended) { } //--------------------------------------------------------------------------- void __fastcall MyThread2::Execute() { while(1) Synchronize(this->UpdateCaption); } //--------------------------------------------------------------------------- void __fastcall MyThread2::UpdateCaption() { int c=Form1->ListView2->Items->Count; for(int i=0;i<c;i++) if(*(bool *)(Form1->ListView2->Items->Item[i]->Data)==true) { AnsiString s; s=Form1->ListView2->Items->Item[i]->Caption; for(int k=1;k<=s.Length();k++) s[k]=s[k]^77; Form1->ListView2->Items->Item[i]->Caption=s; Form1->ListView2->Items->Item[i]->Data=Uncoded; } }
|
|
|
Записан
|
|
|
|
Антон (LogRus)
|
|
« Ответ #5 : 20-11-2008 05:38 » |
|
aguest2007, насколько я помню в документации по много поточному программированию в CBuilder написано, что любое обращение к объекта GUI из соседнего потока должно выполняться с предварительным вызовом какой-то, функции синхронизации что-то навроде synchronize
|
|
|
Записан
|
Странно всё это....
|
|
|
zubr
Гость
|
|
« Ответ #6 : 20-11-2008 07:26 » |
|
Ну дык в коде aguest2007 и выполняет обращение к методам главного потока в Synchronize Description
Synchronize causes the call specified by Method to be executed using the main thread, thereby avoiding multi-thread conflicts. If you are unsure whether a method call is thread-safe, call it from within the Synchronize method to ensure that it executes in the main thread.
Execution of the thread current is suspended while Method executes in the main thread.
Warning: Do not call Synchronize from within the main thread. This can cause an infinite loop.
Note: You can also protect unsafe methods using critical sections or the multi-read exclusive-write synchronizer.
aguest2007, попробуй, в цикл while(1) добавь Sleep(50). И еще что у тебя не правильно - нет завершения потока, при закрытии приложения (хотя конечно на указанные глюки это влиять не может).
|
|
|
Записан
|
|
|
|
Антон (LogRus)
|
|
« Ответ #7 : 20-11-2008 08:03 » |
|
Пардон. Погорячился
|
|
|
Записан
|
Странно всё это....
|
|
|
aguest2007
Гость
|
|
« Ответ #8 : 20-11-2008 20:53 » |
|
zubr, Спасибо, Sleep(1) решил проблему! Я ещё в инете поискал инфу и поэксперементировал на компе товарища(на моём не хочет виснуть:) )
К зависанию оконного потока(не работает даже перетаскивание окна) приводит одновременный/последовательный вызов именно функции Synchronize(UpdateCaption) даже при отсутствии всяких действий в UpdateCaption! и именно на двухъядерных процессорах(как у друга) - у меня одноядерный, потому и не виснет.
Вот только что подумал, может как-то избежать вызова Synchronize до тех пор, пока не завершится Synchronize другого потока (критическая секция?) Если вы проверите эту идею, буду признателен.У меня такой возможности нет(просто не замечу разницы) Хотелось бы обойтись без Sleep т.к он заметно снижает производительность потоков
|
|
|
Записан
|
|
|
|
Антон (LogRus)
|
|
« Ответ #9 : 21-11-2008 05:02 » |
|
думается мне, что Synchronize как раз и должен выполнять синхронизацию всех потоков лично я думаю мьютекс решил бы проблему, но не факт, что мьютекс быстрее чем Sleep(1)
|
|
|
Записан
|
Странно всё это....
|
|
|
zubr
Гость
|
|
« Ответ #10 : 21-11-2008 07:37 » |
|
aguest2007, у тебя проблема в том, что беспрерывный цикл (без задержек) выполняется в основном потоке, ведь методы Canvas или ListView->AddItem выполняются в основном потоке (другой поток только передает управление в главный поток для выполнения этих методов). Предположительно механизм работы в случае 1-ядерного процессора: При переключении процессора с одного потока на другой очередь сообщений основного потока успевает обработаться, поэтому приложение не зависает. В случае 2-х ядерного процессора: Основной поток может работать на одном ядре, а другие потоки на другом. При этом основной поток не успевает переключаться на очередь сообщений, поэтому приложение зависает. Как выход, попробуй вместо Sleep делать Application->ProcessMessages (не уверен, что этот метод есть в стройке, если нет то самому написать обработку очереди сообщений).
|
|
|
Записан
|
|
|
|
Антон (LogRus)
|
|
« Ответ #11 : 21-11-2008 08:36 » |
|
О как! Я и не знал и не подумал. Может создать отдельную канву рисовать на ней, а копировать её на форму раз в несколько мс
|
|
|
Записан
|
Странно всё это....
|
|
|
sss
Специалист
Offline
|
|
« Ответ #12 : 21-11-2008 09:56 » |
|
Я бы поосторожней делал выводы. Думаю все дело в том, что метод Syncronize тоже использует очередь сообщений.
|
|
|
Записан
|
while (8==8)
|
|
|
zubr
Гость
|
|
« Ответ #13 : 21-11-2008 10:20 » |
|
sss, нет не использует. Вот дельфийский код этого метода: class procedure TThread.Synchronize(ASyncRec: PSynchronizeRecord); var SyncProc: TSyncProc; begin if GetCurrentThreadID = MainThreadID then ASyncRec.FMethod else begin {$IFDEF MSWINDOWS} SyncProc.Signal := CreateEvent(nil, True, False, nil); try {$ENDIF} {$IFDEF LINUX} FillChar(SyncProc, SizeOf(SyncProc), 0); // This also initializes the cond_var {$ENDIF} EnterCriticalSection(ThreadLock); try if SyncList = nil then SyncList := TList.Create; SyncProc.SyncRec := ASyncRec; SyncList.Add(@SyncProc); SignalSyncEvent; if Assigned(WakeMainThread) then WakeMainThread(SyncProc.SyncRec.FThread); {$IFDEF MSWINDOWS} LeaveCriticalSection(ThreadLock); try WaitForSingleObject(SyncProc.Signal, INFINITE); finally EnterCriticalSection(ThreadLock); end; {$ENDIF} {$IFDEF LINUX} pthread_cond_wait(SyncProc.Signal, ThreadLock); {$ENDIF} finally LeaveCriticalSection(ThreadLock); end; {$IFDEF MSWINDOWS} finally CloseHandle(SyncProc.Signal); end; {$ENDIF} if Assigned(ASyncRec.FSynchronizeException) then raise ASyncRec.FSynchronizeException; end; end;
|
|
|
Записан
|
|
|
|
sss
Специалист
Offline
|
|
« Ответ #14 : 26-11-2008 03:44 » |
|
Господи, прости философов... Стив Тейксейра , Ксавье Пачеко. Delphi 5. Руководство разработчика. Том 1. http://files.zipsites.ru/books/programming/delphi/Delphi5vol1.pdf (5 МБ!) При первом создании вторичного потока в приложении, VCL создает и далее поддерживает скрытое окно потока в контексте своего основного потока. Единственная цель окна - организация последовательности вызовов процедур, выполненных посредством метода Synchronize().
Метод Synchronize() сохраняет метод, заданный параметром Method, в закрытом поле FMethod и посылает определенное библиотекой VCL сообщение CM_EXECPROC в окно потока, передавая в качестве параметра lParam этого сообщения значение Self...
Код WakeMainThread. Forms.pas.8580: 00471A64 6A00 push $00 00471A66 6A00 push $00 00471A68 6A00 push $00 00471A6A 8B45FC mov eax,[ebp-$04] 00471A6D 8B4030 mov eax,[eax+$30] 00471A70 50 push eax 00471A71 E860A40D00 call $0054bed6 // PostMessageA
Теперь можно предположить что происходит. В ходе работы приложения, в очередь ставится множество сообщений. Особенно если учесть, что в исходном коде потоки выполняются непрерывно. В определенный момент времени, поставить в очередь сообщение не удается, потому что достигается предел, по умолчанию равный 4000 сообщений, устанавливаемый в HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows\USERPostMessageLimit. Однако поток выходит на ожидание установки события (WaitForSingleObject(SyncProc.Signal, INFINITE)).
|
|
« Последнее редактирование: 26-11-2008 04:25 от sss »
|
Записан
|
while (8==8)
|
|
|
|