Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1]   Вниз
  Печать  
Автор Тема: Проблема с многопоточным приложением  (Прочитано 21990 раз)
0 Пользователей и 2 Гостей смотрят эту тему.
aguest2007
Гость
« : 19-11-2008 15:34 » new

Пишу программу на Borland C++ Builder 6
Создаю два потока, которые запускаются/останавливаются через CheckBox, написал, работает, пришел к другу, запустил у него на компе и появилось проблема:
при запуске одного потока всё нормально, а вот когда запускаю одновременно с ним второй, - главное окно намертво виснет, причем на обоих его компах(Windows XP). У меня тоже XP и та же программа работает без проблем.Не пойму, в чем причина?
Записан
Dimka
Деятель
Команда клуба

ru
Offline 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
Деятель
Команда клуба

ru
Offline 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)
Глобальный модератор

ru
Offline Offline
Пол: Мужской
Внимание! Люблю сахар в кубиках!


WWW
« Ответ #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)
Глобальный модератор

ru
Offline Offline
Пол: Мужской
Внимание! Люблю сахар в кубиках!


WWW
« Ответ #7 : 20-11-2008 08:03 » 

Пардон. Погорячился
Записан

Странно всё это....
aguest2007
Гость
« Ответ #8 : 20-11-2008 20:53 » 

zubr, Спасибо, Sleep(1) решил проблему!
Я ещё в инете поискал инфу и поэксперементировал на компе товарища(на моём не хочет виснуть:) )

К зависанию оконного потока(не работает даже перетаскивание окна) приводит одновременный/последовательный вызов именно функции Synchronize(UpdateCaption) даже при отсутствии всяких действий в UpdateCaption! и именно на двухъядерных процессорах(как у друга) - у меня одноядерный, потому и не виснет.

Вот только что подумал, может как-то избежать вызова Synchronize до тех пор, пока не завершится Synchronize другого потока (критическая секция?)
Если вы проверите эту идею, буду признателен.У меня такой возможности нет(просто не замечу разницы)
Хотелось бы обойтись без Sleep т.к он заметно снижает производительность потоков
Записан
Антон (LogRus)
Глобальный модератор

ru
Offline Offline
Пол: Мужской
Внимание! Люблю сахар в кубиках!


WWW
« Ответ #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)
Глобальный модератор

ru
Offline Offline
Пол: Мужской
Внимание! Люблю сахар в кубиках!


WWW
« Ответ #11 : 21-11-2008 08:36 » 

О как! Я и не знал и не подумал.
Может создать отдельную канву рисовать на ней, а копировать её на форму раз в несколько мс
Записан

Странно всё это....
sss
Специалист

ru
Offline 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
Специалист

ru
Offline Offline

« Ответ #14 : 26-11-2008 03:44 » 

Господи, прости философов...

Стив Тейксейра , Ксавье Пачеко. Delphi 5. Руководство разработчика. Том 1.
http://files.zipsites.ru/books/programming/delphi/Delphi5vol1.pdf (5 МБ!)

Цитата: стр. 456
При первом создании вторичного потока в приложении, 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)
Страниц: [1]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines