Проблема с запуском WebService в отдельном потоке в Delphi

Я никогда не задавал вопросов ни в одном сообществе, так как всегда решал проблемы сам или мог найти их в Интернете. Но с этим я зашел в тупик и нуждаюсь в помощи! Чтобы было предельно ясно — я преобразовал простое приложение, найденное в другом месте, чтобы оно использовало объект Tthread. Идея проста — приложение проверяет онлайн через веб-сервис, через компонент THTTPRIO, погоду и заносит результаты в строки Memo1.

Нажав на Button1, мы делаем это стандартно — с помощью THTTPRIO, наложенного на Form1 (он здесь называется htt, как в оригинальном приложении) и с использованием основного и единственного потока.

procedure TForm1.Button1Click(Sender: TObject);
var
wf:WeatherForecasts;
res:ArrayOfWeatherData;
i:integer;
begin
    wf:=(htt as WeatherForecastSoap).GetWeatherByPlaceName(edit1.Text);
    if wf.PlaceName<> '' then
    res:=wf.Details;
    memo1.Lines.Add('The min and max temps in Fahrenheit is:');
    memo1.Lines.Add(' ');
    for i:= 0 to high(res) do
    begin
        memo1.Lines.Add(res[i].Day+'   -   '+ ' Max Temp. Fahr: '+res[i].MaxTemperatureF+'   -   '+'Min Temp Fahr: '+res[i].MinTemperatureF);
    end
end;

Нажимаем на Button2 — используем класс TThread

procedure TForm1.Button2Click(Sender: TObject);
var WFThread:WeatherThread;
begin
  WFThread := WeatherThread.Create (True);
  WFThread.FreeOnTerminate := True;
  WFThread.Place := Edit1.Text;
  WFThread.Resume;
end;

В процедуре Execute в блоке WeatherThread1 я помещаю этот код:

procedure WeatherThread.Execute;
begin
  { Place thread code here }
  GetForecast;
  Synchronize (ShowWeather);
end;

...и код GetForecast:

procedure WeatherThread.GetForecast;
var
    HTTPRIO: THTTPRIO;
    wf:WeatherForecasts;
    res:ArrayOfWeatherData;
    i:integer;
begin
    HTTPRIO := THTTPRIO.Create(nil);
    HTTPRIO.URL := 'http://www.webservicex.net/WeatherForecast.asmx';
    HTTPRIO.WSDLLocation := 'http://www.webservicex.net/WeatherForecast.asmx?WSDL';
    HTTPRIO.Service := 'WeatherForecast';
    HTTPRIO.Port := 'WeatherForecastSoap';

    wf:=(HTTPRIO as WeatherForecastSoap).GetWeatherByPlaceName(Place);

    if Lines=nil then Lines:=TStringList.Create;

    if wf.PlaceName<> '' then
    res:=wf.Details;
    Lines.Clear;
        for i:= 0 to high(res) do
    begin
        Lines.Add(res[i].Day+'   -   '+ ' Max Temp. Fahr: '+res[i].MaxTemperatureF+'   -   '+'Min Temp Fahr: '+res[i].MinTemperatureF);
    end;
end;

Процедура ShowWeather показывает результаты в Form1.Memo1. И теперь есть проблема: В основном потоке, нажав Button1, все работает нормально. Но, конечно, когда компонент HTTPRIO связывается — он замораживает форму.

С Button2 я поместил код в отдельный поток, но он НЕ ХОЧЕТ РАБОТАТЬ! Происходит что-то странное. Когда я запускаю приложение и нажимаю Button2, возникает ошибка при использовании компонента HTTPRIO. Но это работает какое-то время, когда я нажимаю ПЕРВУЮ кнопку 1 и ПОСЛЕ ЭТОГО кнопку 2 (но работает какое-то время, всего 5-7 кликов). Я предполагаю, что делаю что-то не так, но не могу понять, где проблема и как ее решить. Похоже, что код в threaded unit не потокобезопасен, но должен быть. Помогите, пожалуйста, как заставить HTTPRIO работать в потоке!!!

Полный код в архиве можно найти здесь.


person Keyland    schedule 08.08.2010    source источник
comment
Вам нужно быть более конкретным, когда вы говорите, что что-то странное и возникает ошибка. Какая ошибка?   -  person Rob Kennedy    schedule 08.08.2010
comment
Вы понимаете, что у вас есть утечка памяти в вашем WeatherTread.GetForecast? Вы создаете, но никогда не освобождаете экземпляр THTTPRIO.   -  person Marjan Venema    schedule 08.08.2010
comment
Это неправда, @Marjan. Когда THTTPRIO создается без владельца, он использует подсчет ссылок интерфейса для управления временем жизни. Приведение его к типу WeatherForecastSoap создает временную ссылку на интерфейс, которая освобождается в конце функции.   -  person Rob Kennedy    schedule 09.08.2010
comment
Здесь весь код темы? Я не вижу вызова CoInitialize. Delphi обрабатывает вызов при инициализации 'ComObj.pas' для основного потока, но для отдельного потока вы бы назвали его самостоятельно. Кстати, вы за прокси?   -  person Sertac Akyuz    schedule 09.08.2010
comment
@Rob Kennedy: Спасибо, что прояснили это. Я стрелял от бедра, наверное...   -  person Marjan Venema    schedule 09.08.2010


Ответы (3)


Длинный выстрел, но мне не хватает вызовов для синхронизации здесь:

Вы никогда не должны обновлять свой графический интерфейс непосредственно из кода вашего потока.

Вы должны встроить эти вызовы в метод и вызвать этот метод, используя TThread.Synchronize. метод для этого.

В Delphi есть отличная демонстрация.
Начиная с Delphi 4, он включает демонстрацию под названием sortthds.pas в подкаталоге ...\demos\threads, которая показывает то же самое.

--jeroen

person Jeroen Wiert Pluimers    schedule 09.08.2010

Возможно, вы затуманиваете проблему, выполняя динамическое создание RIO (объекты RIO имеют странное время жизни) и связывая их вместе, сравнивая этот результат с простой Button1. Я бы сделал еще одну кнопку, которая вызывает GetForecast без потоков. Посмотрите, работает ли это. Если это бомбит, то ваша проблема не в многопоточности.

person Chris Thornton    schedule 08.08.2010

person    schedule
comment
Не вызывайте его в конструкторе. Конструктор запускается в контексте потока создателя, а не в контексте создаваемого потока. Вызовите обе функции внутри метода Execute. - person Rob Kennedy; 10.08.2010
comment
Большое тебе спасибо! Я только что нашел это сам, снова погуглив, но я вижу, что вы все действительно профессионалы и очень полезны! Ваш быстрый ответ именно то, что работает! Спасибо!!! - person Keyland; 10.08.2010