Delphi Многопоточная проблема

Discussion in 'С/С++, C#, Rust, Swift, Go, Java, Perl, Ruby' started by triblekill, 14 Jan 2018.

  1. triblekill

    triblekill Member

    Joined:
    21 Aug 2011
    Messages:
    351
    Likes Received:
    94
    Reputations:
    1
    По задумке гружу лист в программу с видом ссылок:
    Code:
    кошка:матрёшка
    черепашка:ниндзя
    
    и она выдирает из них окончания после знака : ну и выдирать то она выдирает да только потоки неадекватно себя ведут то потоки берут по две одинаковых строки, то не с начала работают и в конце 4-5 одну и ту же строку берут !

    PHP:
    unit Unit1;

    interface

    uses
      Windows
    MessagesSysUtilsVariantsGraphicsFormssyncobjs,
      
    DialogsStdCtrlsControlsClasses;

    type
      TForm1 
    = class(TForm)
        
    Button1TButton;
        
    Memo1TMemo;
        
    Button2TButton;
        
    Button3TButton;
        
    OpenDialog1TOpenDialog;
        
    Edit1TEdit;
        
    Label1TLabel;
        
    Label2TLabel;
        
    procedure Button1Click(SenderTObject);
        
    procedure Button2Click(SenderTObject);
        
    procedure Button3Click(SenderTObject);
     
    end;
      
    th = class(TThread)
      private
      
    //
      
    public
      
    procedure sync;
      protected
      
    constructor create(createsuspended:boolean);
        
    procedure ExecuteOverride;
    end;

    var
      
    Form1TForm1;
       
    workboolean;
       
    log:integer;
      
    loginststringlist;
      
    cstcriticalsection;
      
    res:string;


    implementation

    {$R *.dfm}

    constructor th.create(createsuspended:boolean);
    begin
    inherited Create
    (CreateSuspended);
    freeonterminate:=true
    end
    ;

    procedure TForm1.Button1Click(SenderTObject);
    begin
    if opendialog1.Execute then
    begin
      logins
    :=tstringlist.Create;
      
    logins.Clear;
      
    logins.LoadFromFile(opendialog1.FileName);
      
    label2.Caption:=inttostr(logins.count-1);
    end;
    end;

    procedure TForm1.Button2Click(SenderTObject);
    var
    threads:integer;
    begin

    if logins.count<>0 then
      Work
    :=True;
      
    log:=-1;
      for 
    threads:=1 to 10 do begin
      th
    .Create(false);
    end;
    end;

    procedure th.Execute;
    begin
    while work do begin
    cs
    .Enter;
    inc(log);
    cs.Leave;
    if 
    work then
    Synchronize
    (sync);
    end;
    end;


    procedure TForm1.Button3Click(SenderTObject);
    begin
    work
    :=false;
    end;

    procedure th.sync;
    begin
    if log=logins.Count-1 then work:=false;

    form1.Memo1.Lines.Add(copy(logins[log],pos(':',logins[log])+1,length(logins[log])));
    form1.Label1.Caption:=inttostr(form1.Memo1.Lines.count-1);
    end;

    initialization
    cs
    :=TCriticalSection.Create;
    work:=false;

    finalization
    cs
    .Free;
    end.
    А с одним потоком for threads:=1 to 1 do th.Create(false); всё работает как часы ! В чём вызвана данная проблема ?
     
  2. DartPhoenix

    DartPhoenix Elder - Старейшина

    Joined:
    15 Sep 2013
    Messages:
    1,106
    Likes Received:
    8,474
    Reputations:
    25
    log у тебя будет рандомным. И будет это все не по порядку а типо 1,5,2,4,3. Этот самый log надо скопировать в локальную переменную потока а не юзать глобально. Ну или передавать в качестве параметра. А чтобы узнать когда треды завершатся - можно уменьшать переменную с количеством общей работы.

    Но вообще все это делается немного по другому... Кодес у тебя суровый очень хоть и небольшой. Практически согласуется с древними индусскими манускриптами :)
     
    triblekill likes this.
  3. triblekill

    triblekill Member

    Joined:
    21 Aug 2011
    Messages:
    351
    Likes Received:
    94
    Reputations:
    1
    Люблю простоту в своих программах =) ещё есть поговорка такая простота - надёжность !
    Я теперь то понял разницу когда log глобальная она передаётся как бы из формы, а не из потока и я заставляю как бы потоки работать с формным log ) но не понимаю как всё поправить точнее как бы понимаю, но лучше бы простым способом без Dec(Threads);
     
    #3 triblekill, 14 Jan 2018
    Last edited: 14 Jan 2018
  4. rudi

    rudi Active Member

    Joined:
    3 Jun 2010
    Messages:
    492
    Likes Received:
    186
    Reputations:
    5
    Есть такое понятие как конкурентные запросы, когда потоки пытаются получить доступ к одному и тому же источнику данных в один и тот же момент времени. Данные в твоем случае должны быть либо блокированы для других потоков - когда какой либо поток с ними работает, то есть если какой либо поток получил доступ к данным, то данные должны быть заблокированы и недоступны для других потоков, другие потоки ожидают когда данные освободятся. Когда поток получил то что хотел, то он освобождает данные, и они становятся доступны для других потоков, но такой алгоритм не очень эфективный если данных много. Лучше заранее нужно разделить данные на равные части и предоставить каждому потоку лишь свой разделенный участок.
    Почитай про мьютексы и про симофоры.
     
  5. triblekill

    triblekill Member

    Joined:
    21 Aug 2011
    Messages:
    351
    Likes Received:
    94
    Reputations:
    1
    Ах вот зачем это условие было нужно, а я его откинул спасибо тебе огромное)))
    if tread<logins.Count then log:=tread else Work:=False; и всё работает )
    tread - глобальная переменная
    log - в раздел public
     
  6. DartPhoenix

    DartPhoenix Elder - Старейшина

    Joined:
    15 Sep 2013
    Messages:
    1,106
    Likes Received:
    8,474
    Reputations:
    25
    Не понял что ты сделал но рад что получилось : )))

    В следующий раз юзай параметры. Тоесть отделяй тред от глобальных переменных полностью.
    "Простота" иногда совсем не простота. Если бы я писал так как ты - я бы уже давно полысел и отнюдь не от простоты. Логика она и в Африке логика.
    Так что если написать с точки зрения гениев "некрасиво" - то все-же можно написать правильно. И никто не виноват что "вы, смертные, меня не понимаете". Эта простота в могилу тебя заведет :)

    И именно для этого делаются якобы "усложнения". Бывают усложнения технические, когда ты треды будишь через APC. Это уже наверное да, подход системщика. Оно круче получается но сложнее.
    А есть именно "усложнения" которые в реале - упрощения. Если тебе надо перебрать список - то берешь обычный среднестатистический цикл и каждому треду даешь из него свое "задание".
    Тоесть в твоем случае передаешь пару логин:пароль. И тред работает непосредственно со своим заданием и не лезет туда, куда его не просят. (это первый уровень скажем так).

    Выполнил задание - возвращаешь результат, юзая мьютекс. Все просто как дверь и надежно. И без багов. И еще проще чем "по простому".
    =================
    Да и... надо наверное предусмотреть: это не будет работать медленнее чем если каждый тред будет брать задание сам. Это будет работать быстрее.
    (просто однажды я так ошибался... решил что нелишне сказать)