Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > Delphi программирование > Общие вопросы Delphi
Регистрация

Восстановить пароль
Повторная активизация e-mail

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 23.12.2019, 17:52   #1
Vlad2891
Пользователь
 
Регистрация: 22.02.2015
Сообщений: 24
Смущение Принципы работы потоков - нужно сделать так чтобы все потоки работали с переменной ID по порядку, но при этом не мешали работе друг друга.

Доброго всем времени суток. Пыхчу над многопоточностью в своей поделке и чем дальше лезу, тем меньше становится понятно. Надеюсь поможете мне прояснить ситуацию и как все устроено.
В общем, сейчас ситуация следующая:
Создаю в unit1 по тыку на кнопку потоки циклом:
Код:
for i := 0 to 5 do begin
  TreadData:= ttread.Create(true);
  TreadData.FreeOnTerminate:=true;
  TreadData.Priority:=tpLower;
  TreadData.Resume;
end;
Внутри потока имеется цикл, в котором все упирается в переменную ID. Чтобы они не переплетались между друг другом я сделал эту переменную общей для всех экземпляров класса:
Код:
class var id:integer;
и вроде все хорошо, потоки трогают ее по порядку, но в тоже время из за этого внутри потока происходит что то мне не понятное, и результаты не совпадают с действительными.
Если объявить переменную ID без "class var", то получается тоже самое что и один поток, только в х-кратном количестве, т.е. переменная id будет обработана, например, 5 раз вместо одного.
Вообще мне нужно сделать так чтобы все потоки работали с переменной ID по порядку, но при этом не мешали работе друг друга. Не могу понять, что я делаю не так, определяю переменные не так, не там, или еще что
Вот, надеюсь понятно изложил :с

Последний раз редактировалось Vlad2891; 23.12.2019 в 17:56.
Vlad2891 вне форума Ответить с цитированием
Старый 23.12.2019, 19:23   #2
Pavia
Лис
Старожил
 
Аватар для Pavia
 
Регистрация: 18.09.2015
Сообщений: 2,409
По умолчанию

Код:
TreadData:= ttread.Create(true);
Здесь ошибка, должно быть
Код:
TreadData:= TTreadData.Create(true);

Цитата:
и вроде все хорошо, потоки трогают ее по порядку, но в тоже время из за этого внутри потока происходит что то мне не понятное, и результаты не совпадают с действительными.
Это называется «гонка процессов» - название так себе и не отражает происходящее. Все общие переменные должны быть защищены инструментами синхронизации. К примеру TInterlocked.Increment(ID); из модуля System.SyncObjs;

Цитата:
Вообще мне нужно сделать так чтобы все потоки работали с переменной ID по порядку, но при этом не мешали работе друг друга.
Мешать друг-другу они будут. Это все равно что всем разом ломиться в одну дверь.
Хорошо поставленный вопрос это уже половина ответа. | Каков вопрос, таков ответ.
У дзен программиста программа делает то что он хотел, а не то что он написал .
Pavia вне форума Ответить с цитированием
Старый 23.12.2019, 22:46   #3
Vlad2891
Пользователь
 
Регистрация: 22.02.2015
Сообщений: 24
По умолчанию

Цитата:
Сообщение от Pavia Посмотреть сообщение
Код:
TreadData:= ttread.Create(true);
Здесь ошибка, должно быть
Код:
TreadData:= TTreadData.Create(true);


Это называется «гонка процессов» - название так себе и не отражает происходящее. Все общие переменные должны быть защищены инструментами синхронизации. К примеру TInterlocked.Increment(ID); из модуля System.SyncObjs;


Мешать друг-другу они будут. Это все равно что всем разом ломиться в одну дверь.
Так вот, если говорить вашими словами, я и хочу сделать чтобы они ломились в эту дверь, но в порядке очереди. На сколько я знаю для этого подходят критические секции, но все равно что то не то.
Спасибо за подсказку, завтра попробую капнуть глубже в сторону TInterlocked.Increment
Vlad2891 вне форума Ответить с цитированием
Старый 24.12.2019, 01:36   #4
northener
ПШП
Участник клуба
 
Регистрация: 15.07.2013
Сообщений: 1,869
По умолчанию

Цитата:
Сообщение от Vlad2891 Посмотреть сообщение
я и хочу сделать чтобы они ломились в эту дверь, но в порядке очереди
Имхо несколько непонятна архитектура задачи. Чем должны заниматься эти многочисленные потоки, если результат своей работы они обязательно выдавать в строгой последовательности?
Ведь суть отдельного потока именно в том что он параллельно работает с другими потоками. И сколько ему ОС выделит времени определяет именно ОС. Вы можете лишь задать приоритет потока причем только в трёх уровнях.
northener вне форума Ответить с цитированием
Старый 24.12.2019, 06:02   #5
Pavia
Лис
Старожил
 
Аватар для Pavia
 
Регистрация: 18.09.2015
Сообщений: 2,409
По умолчанию

Цитата:
Сообщение от Vlad2891 Посмотреть сообщение
Так вот, если говорить вашими словами, я и хочу сделать чтобы они ломились в эту дверь, но в порядке очереди. На сколько я знаю для этого подходят критические секции, но все равно что то не то.
Спасибо за подсказку, завтра попробую капнуть глубже в сторону TInterlocked.Increment
Так это Ваш код надо смотреть, что там да как. А то иначе это гадание по звёздам получается.
Критические секции тоже не плоха себя ведут.
Хорошо поставленный вопрос это уже половина ответа. | Каков вопрос, таков ответ.
У дзен программиста программа делает то что он хотел, а не то что он написал .
Pavia вне форума Ответить с цитированием
Старый 24.12.2019, 10:04   #6
Vlad2891
Пользователь
 
Регистрация: 22.02.2015
Сообщений: 24
По умолчанию

Много чего уже наворотил, по этому выглядит не очень привлекательно.
Это мой модуль потока. Пишу парсер для одного сайта. Там собираются ИД, обладающие необходимыми свойствами и выводятся в листбокс. Сейчас получается так, что "правильные" ид это 1, 5, 9, а в результате получаю 1, 7, 12. Или что то вроде этого. Когда включаю отладку, то вижу что переменные List и http, не освобождаются, из за этого одни и те же данные в List обрабатываются двумя и более потоками и только потом он освобождается
RadioButton'ы отвечают за способ проверки ИД: Случайный в диапазоне или по порядку.
Код:
unit ThreadPars;

interface

uses
 System.Classes,ssl_openssl, httpsend, Windows, Messages, SysUtils, Variants, System.SyncObjs;

type
  parser = class(TThread)
  private
    mes:string;
    class var id:integer;
    procedure UpdateForm1;
    procedure UpdateForm2;
    procedure UpdateForm3;
    procedure UpdateForm4;
  protected
    procedure Execute; override;
  public
    start:boolean;
  end;

implementation
uses Unit1;

{ 
  Important: Methods and properties of objects in visual components can only be
  used in a method called using Synchronize, for example,

      Synchronize(UpdateCaption);  

  and UpdateCaption could look like,

    procedure parser.UpdateCaption;
    begin
      Form1.Caption := 'Updated in a thread';
    end; 
    
    or 
    
    Synchronize( 
      procedure 
      begin
        Form1.Caption := 'Updated in thread via an anonymous method' 
      end
      )
    );
    
  where an anonymous method is passed.
  
  Similarly, the developer can call the Queue method with similar parameters as 
  above, instead passing another TThread class as the first parameter, putting
  the calling thread in a queue with the other thread.
    
}

{ parser }


function Pars(T_, ForS, _T: string): string;
var
a, b: integer;
begin
  Result := '';
  if (T_ = '') or (ForS = '') or (_T = '') then
    Exit;
  a := Pos(T_, ForS);
  if a = 0 then
    Exit
  else
    a := a + Length(T_);
  ForS := Copy(ForS, a, Length(ForS) - a + 1);
  b := Pos(_T, ForS);
  if b > 0 then
    Result := Copy(ForS, 1, b - 1);
end;

procedure parser.Execute;
var
    req:string;
    num,i:integer;
    HTTP: THTTPSend;
    list: TStringList;
begin
  NameThreadForDebugging('Parser');
  CS.Enter;
    id := StrToInt(Form1.Edit2.text);
    inc(id);
  CS.Leave;
  while (work = true) do begin
    HTTP := THTTPSend.Create;
    list := TStringList.Create;
    req:='';
    num := 0;
    CS.Enter;
    if (Form1.RadioButton1.IsChecked) then
    id := Random(2100000000)+100000000;
    if (id < 0) then id := id*-1;
    CS.Leave;
    synchronize(UpdateForm1);
    HTTP.httpmethod('GET','https://some.url/?Id='+intToStr(id)+);
    list.LoadFromStream(http.Document);
    req := list.Text;
    if (http.ResultCode = 200) then begin
    req := pars('Some > ',req,'< Info');
    CS.Enter;
    if (req.Length >= 0) then num := StrToInt(req) else continue;
    mes := req;
    CS.Leave;
    synchronize(UpdateForm2);
      if (num > StrToInt(Form1.Edit1.Text)) then begin
        synchronize(UpdateForm3);
      end else begin
        synchronize(UpdateForm4);
      end;
    end;

    if (Form1.RadioButton2.IsChecked) then begin
      CS.Enter;
        TInterlocked.Increment(ID);
      CS.Leave;
    end;
    HTTP.Free;
    list.Free;
  end;
end;

procedure parser.UpdateForm1;
begin
form1.label6.Text := 'ID: '+intToStr(id);

end;

procedure parser.UpdateForm2;
begin
form1.label5.Text := 'Comment: ' + mes;
end;

procedure parser.UpdateForm3;
begin
  Form1.Listbox1.Items.Add(IntToStr(id));
  Form1.label2.Text:='Good: '+IntToStr(Form1.Listbox1.Items.Count);
  Form1.Edit2.Text := IntToStr(id);
end;

procedure parser.UpdateForm4;
begin
  Form1.Listbox2.Items.Add(IntToStr(id));
  Form1.label4.Text:='Bad: '+IntToStr(Form1.Listbox2.Items.Count);
end;

end.

Последний раз редактировалось Vlad2891; 24.12.2019 в 10:07.
Vlad2891 вне форума Ответить с цитированием
Старый 25.12.2019, 10:12   #7
Pavia
Лис
Старожил
 
Аватар для Pavia
 
Регистрация: 18.09.2015
Сообщений: 2,409
По умолчанию

Я даже не знаю что тут сказать... Всё очень плохо.

Допустим у нас 3 потока 1 главный 2 и 3 которые выполняют работу.
1)
Код:
HTTP.httpmethod('GET','https://some.url/?Id='+intToStr(id)+);
Тут ID не экранирован поэтому возможен случай когда разные потоки будут читать с одним ID одну и туже страницу.

Второй поток уснул после CS.Leave; третий изменил ID и второй проснулся они оба читают страницу с одинаковым ID.
2)
Тут опять таки ID не экранирован
Код:
Form1.Listbox1.Items.Add(IntToStr(id));
Так как воторой поток выполняет его synchronize то по сути он спит и работает главный поток (1-поток)
Но 3 потоку ничего не мешает изменить ID.
3) TInterlocked.Increment(ID); - увеличивает ID на 1.
Поэтому у вас цифры и не совпадают.

Третья проблема решается легко убрав TInterlocked.Increment(ID); у вас и так есть критическая секция.

Если вторую проблему легко решить
Код:
CS.Enter;
Form1.Listbox1.Items.Add(IntToStr(id));
CS.Leave;
и так для всех четырёх функций вывода.

То с первой так не выйдет. Поэтому предлагаю переписать весь код на TParallel.For
http://docwiki.embarcadero.com/RADSt...amming_Library
Хорошо поставленный вопрос это уже половина ответа. | Каков вопрос, таков ответ.
У дзен программиста программа делает то что он хотел, а не то что он написал .

Последний раз редактировалось Pavia; 25.12.2019 в 10:15.
Pavia вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц



Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Как сделать, чтобы клетки не заходили друг на друга Роман12212221 C# (си шарп) 0 06.12.2017 15:55
Как сделать чтоб столбцы равнялись друг на друга bodalev HTML и CSS 2 12.05.2015 21:18
Uses. Правильно обьявить 2 класса, чтобы те взаимно использовали друг-друга. Человек_Борща Общие вопросы Delphi 3 03.06.2012 08:06
как реализовать чтобы при нажатии прямоугольники меняли цвета не зависимо друг от друга programmm Win Api 0 18.05.2011 17:50