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

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

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

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

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

Ответ
 
Опции темы Поиск в этой теме
Старый 10.09.2015, 00:01   #1
_ZixeL_
Форумчанин
 
Регистрация: 04.11.2012
Сообщений: 151
По умолчанию Синхронизация потоков

Приветствую. Ребята, требуется помощь.
В потоках нуб, признаюсь. Пытаюсь синхронизировать потоки.

Создаю потоки так:
Код:
var
  Form1: TForm1;
  Thread, Pr:integer;
  Work:boolean;
  CS:TcriticalSection;
  nick:string;

...

procedure TForm1.Button1Click(Sender: TObject);
begin
 Pr:=1;
 Work:=true;
 for Thread:=1 to 40 do
 TNewThread.Create(false);
end;

Сам код:
Код:
type
  TNewThread = class(TThread)
  private
    page:string;
    Rez:integer;
  protected
    procedure Execute; override;
  public
    procedure Sync;
    constructor Create(CreateSuspended: Boolean);
  end;

implementation
Uses Unit1;



constructor TNewThread.Create(CreateSuspended: Boolean);
begin
  inherited Create(CreateSuspended);
end;

procedure TNewThread.Execute;
var HTTP:TidHTTP;
    CurAcc:integer;
begin

 while Work do
  begin
     CS.Enter;

       Inc(Pr);

       if Pr<1000 then Work:=true else Work:=false;

     CS.Leave;

      if Work then
        begin
        HTTP:=TidHTTP.create;
        HTTP.HandleRedirects:=true;

        try
         page:=HTTP.Get('http://***.ru/members/'+inttostr(Pr)+'/');
         if Pos('username', page)>0 then  nick:=Pars('class="username">', page, '</h1>');
        except


        end;
         HTTP.Free;
         Synchronize(Sync);

        end;


 end;

dec(Thread);
if Thread=0 then ShowMessage('OK');
end;


procedure TNewThread.Sync;
begin
  Form1.Memo1.Lines.Add(nick);
end;
Получается в блокноте всё комом. Начинает не с первой страницы парсить + много повторов.
Не обессудьте, с делфи мало работаю.
Спасибо.

Последний раз редактировалось _ZixeL_; 10.09.2015 в 00:29.
_ZixeL_ вне форума Ответить с цитированием
Старый 10.09.2015, 01:27   #2
ДралсяСошибками
Форумчанин
 
Аватар для ДралсяСошибками
 
Регистрация: 31.05.2011
Сообщений: 301
По умолчанию

Не уверен, что правильно, не тестил, но как-то так
Код:
var
  CriticalSection1: TCriticalSection;
  Count: Integer;

type
  TNewThread = class(TThread)
  private
    Name: String;
  protected
    procedure Execute; override;
    procedure Sync;
  end;

procedure TForm1.Button3Click(Sender: TObject);
var
  NewThread: Array[0..9] of TNewThread;
  i: Integer;
begin
 for i := Low(NewThread) to High(NewThread) do
   begin
     Inc(Count);
     TNewThread.Create(False);
   end;
end;

{ TNewThread }

procedure TNewThread.Execute;
var
  IdHTTP: TIdHTTP;
  SL: TStringList;
begin
  FreeOnTerminate := True;
  SL := TStringList.Create;
  IdHTTP := TIdHTTP.Create;
  IdHTTP.HandleRedirects := True;
    while Count < 1000 do
      begin
        SL.Text := IdHTTP.Get('http://***.ru/members/' + IntToStr(Count) + '/');
          if Pos('username', SL.Text) <> 0 then Name := Pars('class="username">', SL.Text, '</h1>')
          else Name := '---';
        SL.Clear;
        Synchronize(Sync);
      end;
  IdHTTP.Free;
  SL.Free;
end;

procedure TNewThread.Sync;
begin
  CriticalSection1.Enter;
  Form1.Memo1.Lines.Add(IntToStr(Count) + #9 + Name);
  Inc(Count);
  CriticalSection1.Leave;
end;

initialization
  CriticalSection1 := TCriticalSection.Create;
finalization
  CriticalSection1.Free;;

end.
ДралсяСошибками вне форума Ответить с цитированием
Старый 10.09.2015, 01:56   #3
_ZixeL_
Форумчанин
 
Регистрация: 04.11.2012
Сообщений: 151
По умолчанию

Цитата:
Сообщение от ДралсяСошибками Посмотреть сообщение
Не уверен, что правильно, не тестил, но как-то так
Спасибо большое за ответ. Но, к сожалению, проблема всё та же: начинается парсить не спервой страницы + первые 10 - повторы.
И потоки обрываются. Пропарсило 30 страниц и заглохло
+ Рассинхронизация. Ид страницы не соотвествует нику.

Последний раз редактировалось _ZixeL_; 10.09.2015 в 02:06.
_ZixeL_ вне форума Ответить с цитированием
Старый 10.09.2015, 03:08   #4
ДралсяСошибками
Форумчанин
 
Аватар для ДралсяСошибками
 
Регистрация: 31.05.2011
Сообщений: 301
По умолчанию

Попробуй такой вариант)))
Код:
var
  CriticalSection1: TCriticalSection;
  Count: Integer;

type
  TNewThread = class(TThread)
  private
    Name: String;
    i: Integer;
  protected
    procedure Execute; override;
    procedure Sync;
  end;

procedure TForm1.Button3Click(Sender: TObject);
var
  NewThread: Array[0..9] of TNewThread;
  i: Integer;
begin
  Inc(Count);
    for i := Low(NewThread) to High(NewThread) do TNewThread.Create(False);
end;

{ TNewThread }

procedure TNewThread.Execute;
var
  IdHTTP: TIdHTTP;
  SL: TStringList;
begin
  FreeOnTerminate := True;
  SL := TStringList.Create;
  IdHTTP := TIdHTTP.Create;
  IdHTTP.HandleRedirects := True;
    while Count < 1000 do
      begin
        CriticalSection1.Enter;
        i := Count;
        Inc(Count);
        CriticalSection1.Leave;
        SL.Text := IdHTTP.Get('http://***.ru/members/' + IntToStr(i) + '/');
          if Pos('username', SL.Text) <> 0 then Name := Pars('class="username">', SL.Text, '</h1>')
          else Name := '---';
        SL.Clear;
        Synchronize(Sync);
      end;
  IdHTTP.Free;
  SL.Free;
end;

procedure TNewThread.Sync;
begin
  CriticalSection1.Enter;
  Form1.Memo1.Lines.Add(IntToStr(i) + #9 + Name);
  CriticalSection1.Leave;
end;

initialization
  CriticalSection1 := TCriticalSection.Create;
finalization
  CriticalSection1.Free;;

end.
Потестил на ya.ru, 99 значений, получил результат:
Код:
1	---
3	---
9	---
4	---
5	---
2	---
7	---
8	---
11	---
10	---
6	---
19	---
17	---
13	---
14	---
15	---
20	---
18	---
21	---
16	---
22	---
12	---
25	---
26	---
23	---
27	---
28	---
31	---
30	---
36	---
35	---
32	---
34	---
38	---
24	---
37	---
40	---
41	---
39	---
43	---
46	---
42	---
29	---
47	---
44	---
45	---
50	---
49	---
51	---
52	---
55	---
57	---
59	---
58	---
48	---
62	---
54	---
53	---
64	---
60	---
63	---
66	---
65	---
71	---
68	---
69	---
70	---
72	---
33	---
74	---
76	---
56	---
77	---
61	---
80	---
73	---
81	---
82	---
67	---
78	---
83	---
85	---
79	---
87	---
88	---
75	---
92	---
91	---
86	---
94	---
96	---
84	---
93	---
95	---
97	---
98	---
90	---
99	---
89	---
По порядку они естественно не будут, так как время загрузки страницы у каждого потока разное)))

Последний раз редактировалось ДралсяСошибками; 10.09.2015 в 03:26.
ДралсяСошибками вне форума Ответить с цитированием
Старый 10.09.2015, 03:27   #5
_ZixeL_
Форумчанин
 
Регистрация: 04.11.2012
Сообщений: 151
По умолчанию

Цитата:
Сообщение от ДралсяСошибками Посмотреть сообщение
Попробуй такой вариант)))
К сожалению, ничего не изменилось.

UPD.
try...exept..end; помогло
Вроде всё работает))
Спасибо огромное за помощь!

Последний раз редактировалось _ZixeL_; 10.09.2015 в 03:32.
_ZixeL_ вне форума Ответить с цитированием
Старый 10.09.2015, 03:50   #6
DIONISKA
Форумчанин
 
Регистрация: 07.11.2011
Сообщений: 161
По умолчанию

Цитата:
Вроде всё работает))
Нагородили тут с три короба

сделали-бы лучше так:

Код:
  TNewThread = class(TThread)
  private
    Name: String;
    fmin,fmax :int64;
    id:integer;
    userlist:tstringlist;
  protected
    procedure Execute; override;
    procedure Sync;
  public
    constructor create(crtsuspended:boolean; minvalue,maxvalue:int64);

  end;


  TForm1 = class(TForm)
  ...
    procedure updatememo;
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var

  CriticalSection1: TCriticalSection;
  Count: Integer;
  MyList:tstringlist;
  THL:TThreadlist;
  Form1: TForm1;

implementation

{ TNewThread }

constructor TNewThread.create(crtsuspended: boolean; minvalue,
  maxvalue: int64);
begin
inherited create(crtsuspended);
 self.fmin:=minvalue;
 self.fmax:=maxvalue;
end;

procedure TNewThread.Execute;
 var
  IdHTTP: TIdHTTP;
  SL: TStringList;
  i:integer;
  begin

    FreeOnTerminate := True;
    SL := TStringList.Create;
    userlist:=tstringlist.Create;
    IdHTTP := TIdHTTP.Create;
    IdHTTP.HandleRedirects := True;
    for I := self.fmin to self.fmax do
    begin
          SL.Text := IdHTTP.Get('http://***.ru/members/' + IntToStr(i) + '/');
            if Pos('username', SL.Text) <> 0 then Name := Pars('class="username">', SL.Text, '</h1>')
            else Name := '---';
          SL.Clear;
          userlist.Add(inttostr(i)+'|'+name);
        end;
   Synchronize(Sync);
   SL.Free;
   IdHTTP.Free;
   userlist.Free;
end;

procedure TNewThread.Sync;
begin
  CriticalSection1.Enter;
  Mylist.AddStrings(userlist);
  Mylist.Sort;
  CriticalSection1.Leave;
end;

procedure TForm1.Button1Click(Sender: TObject);
var i:integer;
    step:integer;
    NewThread: array [0..9] of TNewThread;
begin
step:=round(1000/10);
for I := 0 to 9 do
NewThread[i]:=(TNewThread.create(false,i*step,i+1*step));
end;

procedure TForm1.updatememo;
begin
self.Memo1.Clear;
self.Memo1.Lines.AddStrings(MyList);
end;

initialization
  CriticalSection1 := TCriticalSection.Create;
  THL:=TThreadlist.Create;
  MyList:=tstringlist.Create;

finalization
  CriticalSection1.Free;
  thl.Free;
  mylist.Free;
И никакой каши или повторений, а stringlist-бы ещё и по номерам отсортировал вконце

Последний раз редактировалось DIONISKA; 10.09.2015 в 03:57.
DIONISKA вне форума Ответить с цитированием
Старый 10.09.2015, 10:37   #7
evg_m
Старожил
 
Регистрация: 20.04.2008
Сообщений: 5,526
По умолчанию

Цитата:
Получается в блокноте всё комом. Начинает не с первой страницы парсить + много повторов.
Суть потоков в независимом и неопределенном порядке исполнения, а значит ПОРЯДОК ВЫВОДА не может быть заранее определен.
Если важен порядок получения и потоки ДОЛЖНЫ будут исполняться в нужном порядке, то зачем потоки, Вполне достаточно цикла.
Если НЕ важен порядок получения, НО ВАЖЕН порядок вывода, то НЕ НАДО пихать в поток то чему там В ЛЮБОМ случае не место(а именно вывод в мемo).
Если его не будет, то не нужен будет ни syncronize, ни CriticalSection. (кстати Synchronize сам по себе УЖЕ CriticalSection)
т.е.
Код:
 Syncronize(sync);
procedure Sync;
begin
  CritcalSection; ///   масло масляное!!!!
end;
Достаточно чтобы главная программа знала обо всех своих потоках.
потоки должны получить данные и СПОКОЙНО ЖДАТЬ в завершенном(!) виде(FreeOnTreminate=FALSE!!!) пока их данные потребует главный поток( и что он с ними делать будет выводить в Мемо/сначала сортировать потов выводить ListBox/еще что-то не должно волновать поток)
ПРИНЦИП разделяй и властвуй: при изменении способа вывода (memo|....) способ получения (ПОТОКИ) остаются НЕИЗМЕННЫМИ.

чтобы узнать что поток завершил работу, у него есть event OnTerminate!!!

Код:
var
  NewThread: Array[0..9] of TNewThread; // МЫ ДОЛЖНЫ помнить о своих потоках 
поэтому ЭТО (или поле Формы или ГЛОБАЛЬНАЯ переменная)

procedure TForm1.Button3Click(Sender: TObject);
var
  i: Integer;
  r: TNewTread;
begin
    for i := Low(NewThread) to High(NewThread) do begin
      r:=TNewThread.Create;//создаем поток БЕЗ его ЗАПУСКА (СМОТРИ TNewThread.Create)
      //  и спокойно настраиваем его (при желании настройку можно перенести в constructor !!!!)
      r.OnTerminate:=DoAfterLoad;//что делать когда поток закончиться 
      r.page:=i; // c какой страницей(-ами) ему ПРЕДСТОИТ работать
      r.Resume; // и когда ВСЕ готово ЗАПУСКАЕМ его
      array[i]:=r; //ЗАПОМНИЛИ его А это можно сделать в любое ВРЕМЯ (хоть до запуска хоть ДАЖЕ и до настройки СРАЗУ после создания)
    end;
end;

procedure TForm1.DoAfterLoad(sender: TObject); //НУ и что мы будем делать когда какой-либо  поток ВЫПОЛНИТ порученное ему дело???
var
   r: TNewThread;
   frunthread: boolean;
begin
   r:=sender as TNewThread; //если НАМ нужно узнаем КАКОЙ ИМЕННО поток нам сигнализирует
   Memo1.Lines.Addd(r.List); // МОЖЕМ просто добавить данные из ЗАВЕРШЕННОГО потока туда куда считаем нужным

   // МОЖЕТ просто выводить ДАННЫЕ от ВСЕХ завершенных потоков 
   Memo.Clear; // каждый раз заново не мучаясь вставкой новых в нужно место
   for i := Low(NewThread) to High(NewThread) do begin
      r:=NewThread[i]; // берем очередной поток 
      if r.Terminated //как-то УЗНАЕМ о его состоянии и если он готов
          then  Memo.Lines.Add(r.List); // ВЫВОДИМ его данные
   end;

 // ну и НАКОНЕЦ когда приходим к выводу(пониманию) что все потоки завершились можем наконец почистить
   for i := Low(NewThread) to High(NewThread) do begin
      r:=NewThread[i];
      NewThread[i]:=nil;
      r.Free;
   end;
end;
P.S. НЕ обязательно ВСЕ это и в таком виде должно быть в твоей программе.
читай комментарии и ПИШИ(хотя бы мысленно!!!) свои к своему коду.

Ну и сам поток работающий без ВСЯКОЙ синхронизации
Код:
TNewThread = class(TThread)
  protected
    procedure Execute; override;
  public
    constructor create;
    destructor Destroy; override;
  public // ПРАВИЛЬНО конечно же будет property c нужным доступом read | write
     list: TStringList; //здесь будут полученные данные
     page: integer; //здесь мы укажем страницу для загрузки
  end;

constructor TNewThread.Create;
begin
   inherited Create(true); //СОЗДАНИЕ в ОСТАНОВЛЕННОМ виде
   list:=TStringList.Create;
end;

destructor TNewThread.Destroy;
begin
   list.Free; //если мы что создали мы должны это уничтожить
   inherited;
end;

procedure TNewThread.Execute;
 var
  IdHTTP: TIdHTTP;
  SL: TStringList;
begin

///    FreeOnTerminate := True; НИ В КОЕМ РАЗЕ!!!
    IdHTTP := TIdHTTP.Create;
    IdHTTP.HandleRedirects := True;
    SL := TStringList.Create;

    try //по хорошему тону (далее смотри finally)

// КАК-то заполняем List (наши  ВНУТРЕННИЕ результаты)
          SL.Text := IdHTTP.Get('http://***.ru/members/' + IntToStr(page) + '/');
            if Pos('username', SL.Text) <> 0 then Name := Pars('class="username">', SL.Text, '</h1>')
            else Name := '---';
          SL.Clear;

      list.Add(inttostr(page)+'|'+name);  !!! 
//////////////////////////////////////////////////////

  finally // мы  ДОЛЖНЫ обеспечить безусловное уничтожение локально(!) созданного ДАЖЕ при сбоях 
    sl.Free;
   IdHTTP.Free;
  end;

end;
программа — запись алгоритма на языке понятном транслятору

Последний раз редактировалось evg_m; 10.09.2015 в 10:58.
evg_m вне форума Ответить с цитированием
Старый 10.09.2015, 17:24   #8
_ZixeL_
Форумчанин
 
Регистрация: 04.11.2012
Сообщений: 151
По умолчанию

Всё работает. Всем большое спасибо.
Только одна проблемка. В строке
Код:
if Pos('username', SL.Text) <> 0 then Name := Pars('class="username">', SL.Text, '</h1>') else Name := '---';
не работает else. То есть, никак не могу вести статистику страниц с ошибками ( пробовал подставлять переменные, пробовал менять условия поиска - ничего не помогает ). Если на странице ошибка и, как в коде, прописано Name := '---'; - оно просто пропускает и не добавляет в "---" в лист.
Так же пытался, в самом теле потока, вести статистику пропарсеных страниц ( что-то вроде: check:=check+1 ). Но так же к успеху не пришёл
Есть какие-нибудь решения как вести статистику ( пропарсено, успешных страниц, страниц с ошибками)? Буду весьма признателен за наводку.

Последний раз редактировалось _ZixeL_; 10.09.2015 в 17:55.
_ZixeL_ вне форума Ответить с цитированием
Старый 10.09.2015, 18:11   #9
evg_m
Старожил
 
Регистрация: 20.04.2008
Сообщений: 5,526
По умолчанию

Поток САМ по себе НИЧЕГО не суммирует, а только устанавливает СВОИ(твои) флажки(спарсено, нашли ошибку, завершено, ....)
(terminated это тот же флажок который можно использовать для определения завершения работы потока ).
А всю статистику ВЕДЕТ основная программа.

Для МОЕГО варианта.
У тебя есть список потоков.
В нем ты можешь КАЖДЫЙ раз при завершении потока (DoAfterLoad)

пробегать по списку и СЧИТАТЬ любую статистику, используя ДАННЫЕ от потоков.
(смотри Упорядоченный вывод результатов завершенных(!) потоков)
Код:
if r.terminated then завершено:=завершено+1;
if r.errorList then ошибка:=ошибка+1; // Конечно же в потоках ты ДОЛЖЕН объявить и УСТАНАВЛИВАТЬ данную переменную!!
Как устанавливать это?
Ну это как раз надо смотреть TNewThread.Execute
поскольку ВСЕ действия потока(изменения его данных) (возможно кроме terminated) ВЫПОЛНЯТСЯ там.

Цитата:
Если на странице ошибка и
Это означает что? что она не может быть прочитана?
В этом случае до Pos (и соответствено ни до then ни до else ) дело может и не доходить. В дело вступает Exception. и сразу за ним treminate (потока)!!
можно сделать так
Код:
Name:='---'; /// "предустановку" ошибки.
и потом только
читать;
парсить;
и уcтанавливать УСПЕШНЫЙ результат (name:=....)
программа — запись алгоритма на языке понятном транслятору

Последний раз редактировалось evg_m; 10.09.2015 в 18:25.
evg_m вне форума Ответить с цитированием
Старый 10.09.2015, 18:15   #10
_ZixeL_
Форумчанин
 
Регистрация: 04.11.2012
Сообщений: 151
По умолчанию

Цитата:
Сообщение от evg_m Посмотреть сообщение
Для МОЕГО варианта.
К сожалению, Ваш вариант слишком сложен для меня, я до такого уровня ещё не дорос. Пытался, но у меня Ваш вариант не заработал
Можете привести пример для варианта DIONISKA или ДралсяСошибками ?
_ZixeL_ вне форума Ответить с цитированием
Ответ


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



Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Синхронизация потоков Fireblade-fan Общие вопросы Delphi 5 17.12.2012 01:57
Синхронизация потоков _Bers Общие вопросы C/C++ 5 23.12.2011 22:57
Синхронизация потоков добрый_фей Помощь студентам 5 09.12.2011 19:57
Синхронизация потоков alenka_ej Помощь студентам 0 03.06.2010 22:20