![]() |
|
|
Регистрация | << Правила форума >> | FAQ | Пользователи | Календарь | Поиск | Сообщения за сегодня | Все разделы прочитаны |
![]() |
|
Опции темы | Поиск в этой теме | Опции просмотра |
#1
|
|||
|
|||
![]() В общем считаю нужным создать другую тему, потому как название старой уже совсем не соответствует возникшим вопросам. Написал рабочий однопоточный проект, теперь пытаюсь сделать его многопоточным, прочитал уже кучу инфы, но что-то не выходит у меня. Вроде бы получается создать потоки, но дальше вылетает ошибка "Socket Error. Socket is not connected". Хотя в один поток всё работает отлично. Я так понимаю. что нужно как-то синхронизировать потоки между собой. На данный момент код выглядит так:
Код:
unit Unit2; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, sSkinManager, StdCtrls, sButton, ComCtrls, acProgressBar, sMemo, sLabel, sEdit, IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient, IdHTTP, IdIOHandler, IdIOHandlerSocket, IdIOHandlerStack, IdSSL, IdSSLOpenSSL, IdCookieManager, IdIntercept, IdCompressionIntercept, sDialogs, sSpinEdit; type TForm2 = class(TForm) sMemo1: TsMemo; sButton1: TsButton; IdHTTP1: TIdHTTP; IdSSLIOHandlerSocketOpenSSL1: TIdSSLIOHandlerSocketOpenSSL; sEdit3: TsEdit; sLabel3: TsLabel; sLabel4: TsLabel; sLabel5: TsLabel; sMemo2: TsMemo; sButton2: TsButton; sOpenDialog1: TsOpenDialog; sLabel6: TsLabel; sLabel7: TsLabel; sLabel8: TsLabel; sLabel9: TsLabel; sSkinManager1: TsSkinManager; sProgressBar1: TsProgressBar; sButton3: TsButton; sSpinEdit1: TsSpinEdit; sLabel1: TsLabel; procedure sButton1Click(Sender: TObject); procedure sButton2Click(Sender: TObject); procedure sButton3Click(Sender: TObject); { Private declarations } public { Public declarations } end; type TMyThread = class(TThread) private protected procedure WriteFile; procedure Good; procedure Bad; procedure ProgressBar; procedure MemoClear; procedure Execute; override; end; var Form2: TForm2; Accounts: TStringList; Login, Password, del: string; ProgressPos: integer; T1: TThread; implementation {$R *.dfm} procedure TForm2.sButton2Click(Sender: TObject); begin Accounts:=TStringList.Create; if sOpenDialog1.Execute then begin Accounts.Clear; sMemo1.Lines.Clear; sMemo2.Lines.Clear; Accounts.LoadFromFile(sOpenDialog1.FileName); sLabel9.Caption:=inttostr(Accounts.Count); end; if pos(';', Accounts.Strings[0])<>0 then begin del:=';'; end else begin del:=':'; end; end; procedure TForm2.sButton3Click(Sender: TObject); begin T1.Terminate; Form2.sProgressBar1.Position:=Accounts.Count; end; procedure TForm2.sButton1Click(Sender: TObject); {var Threads: integer; begin for Threads:=1 to sSpinEdit1.Value do} begin T1:=TMyThread.Create(False); T1.Priority:=tpLower; end; {end;} procedure TMyThread.MemoClear; begin Form2.sMemo1.Lines.Clear; Form2.sMemo2.Lines.Clear; end; procedure TMyThread.ProgressBar; begin Form2.sProgressBar1.Position:=ProgressPos+1; Form2.sProgressBar1.Max:=Accounts.Count; end; procedure TMyThread.Bad; begin Form2.sMemo2.Lines.Add(Login+del+Password); Form2.sLabel6.Caption:=inttostr(Form2.sMemo2.Lines.Count); end; procedure TMyThread.Good; begin Form2.sMemo1.Lines.Add(Login+del+Password); Form2.sLabel7.Caption:=inttostr(Form2.sMemo1.Lines.Count); end; procedure TMyThread.WriteFile; begin Form2.sMemo1.Lines.SaveToFile(ExtractFilePath(Application.ExeName) + 'Good.txt'); Form2.sMemo2.Lines.SaveToFile(ExtractFilePath(Application.ExeName) + 'Bad.txt'); end; procedure TMyThread.Execute; var HTML: string; Acc: integer; begin; Synchronize(MemoClear); for Acc := 0 to Accounts.Count-1 do begin if Terminated then break; ProgressPos:=Acc; Synchronize(ProgressBar); Login:=copy(Accounts[Acc], 1, pos(del, Accounts[Acc])-1); Password:=copy(Accounts[Acc], pos(del, Accounts[Acc])+1, MaxInt); HTML:=Form2.IdHTTP1.Get('http://xxxxxx'+Login+'xxxxxx'+Password); if pos ('class="xxxxxx"', HTML)<>0 then if Form2.sEdit3.Text<>'' then begin HTML:=Form2.IdHTTP1.Get('https://xxxxxx'+Form2.sEdit3.Text+'xxxxxx'); if pos ('class="xxxxxx"', HTML)<>0 then begin Synchronize(Bad); end else begin Synchronize(Good); end end else begin Synchronize(Good); end else begin Synchronize(Bad); end; Synchronize(WriteFile); end; end; end. Последний раз редактировалось AlexBerg001, 23.04.2015 в 14:01. |
#2
|
|||
|
|||
![]() Ох ты ж елки...
Естесвенно, не работает. Либо ты делаешь ограничение на доступ к IdHTTP из потока (см. CriticalSection), либо у каждого потока надо создать свой экземпляр IdHTTP. Да и вообще, если просто хочешь обрабатывать список в несколько потоков, надо менять внутреннюю архитектуру приложения. Кратко: 1. Основной поток создает очередь заданий, пул потоков (не рекомендую создавать больше 10 для начала), запускает потоки и ждет пока они все не обработают (тут есть варианты реализации, в зависимости от того, что хочется). 2. Поток смотрит в очередь (CriticalSection), если там есть задание, то берет одно и начинает его выполнять, по результату отправляет сообщение в основной поток (опять CriticalSection или использовать виндовые сообщения). Если заданий нет, то либо засыпает на какое-то время, либо "умирает" (зависит от реализации хотелок). 3. Основной поток при получении сообщения от потока выводит результат пользователю. |
#3
|
|||
|
|||
![]() Вы конечно всё замечательно объяснили, но у меня что-то не выходит создать IdHTTP в потоке, дальше я ещё не продвинулся. Можете поконкретнее объяснить как это можно реализовать? Это вообще мой первый проект, вот пытаюсь как-то сделать что-то толковое)
И почему "ох ты ж ёлки"? Разве криво написано для однопотока? И ещё одна беда, когда в глобальные var добавляю CS: TCriticalSection, в OnCreate формы CS:=TCriticalSection.Create вылезает ошибка Undeclared Identifier TCriticalSection. В чём ошибка-то не пойму? Последний раз редактировалось AlexBerg001, 23.04.2015 в 14:42. |
#4
|
|||
|
|||
![]() Короче разобрался немного вроде, просто добавил не TCriticalSection, а TRTLCriticalSection, получилось вот так:
Код:
var Form2: TForm2; Accounts: TStringList; Login, Password, del: string; ProgressPos: integer; T1: TThread; CS: TRTLCriticalSection; procedure TForm2.FormCreate(Sender: TObject); begin CS:=TRTLCriticalSection.Create; end; |
#5
|
||||
|
||||
![]() потому что:
Код:
_RTL_CRITICAL_SECTION = record DebugInfo: PRTLCriticalSectionDebug; LockCount: Longint; RecursionCount: Longint; OwningThread: THandle; LockSemaphore: THandle; Reserved: DWORD; end; TRTLCriticalSection = _RTL_CRITICAL_SECTION; Пишу программы за еду. __________________ |
#6
|
|||
|
|||
![]() Цитата:
|
#7
|
||||
|
||||
![]() это ответ почему компилятор не хочет делать то, что задумал программист. подробности можно прочитать в любой книжке. просто если сейчас не разобраться с этим, то будущее очень туманно.
Пишу программы за еду. __________________ |
#8
|
|||
|
|||
![]() Короче, вроде создал я критическую секцию эту, будь она неладна, через Initialize, а не Create))) В общем с потоком получилось у меня вот так:
Код:
procedure TMyThread.Execute; var HTML: string; Acc: integer; begin; for Acc := 0 to Accounts.Count-1 do begin if Terminated then break; ProgressPos:=Acc; Synchronize(ProgressBar); Login:=copy(Accounts[Acc], 1, pos(del, Accounts[Acc])-1); Password:=copy(Accounts[Acc], pos(del, Accounts[Acc])+1, MaxInt); EnterCriticalSection(CS); HTML:=Form2.IdHTTP1.Get('http://xxxxxx'+Login+'xxxxxx'+Password); if pos ('xxxxxx', HTML)<>0 then if Form2.sEdit3.Text<>'' then begin HTML:=Form2.IdHTTP1.Get('https://xxxxxx'+Form2.sEdit3.Text+'xxxxxx'); LeaveCriticalSection(CS); if pos ('xxxxxx', HTML)<>0 then begin Synchronize(Bad); end else begin Synchronize(Good); end end else begin Synchronize(Good); end else begin Synchronize(Bad); end; Synchronize(WriteFile); end; end; Последний раз редактировалось AlexBerg001, 25.04.2015 в 02:15. |
#9
|
|||
|
|||
![]() Очень сложно для меня
![]() |
#10
|
|||
|
|||
![]() Ну или вот второй вариант без критической секции
Код:
procedure TMyThread.Execute; var HTML: string; Acc: integer; IdHTTP: TIdHTTP; IdSSL: TIdSSLIOHandlerSocketOpenSSL; begin; IdHTTP := TIdHTTP.Create; IdSSL := TIdSSLIOHandlerSocketOpenSSL.Create; IdHTTP.HandleRedirects:=true; IdHTTP.IOHandler:=IdSSL; for Acc := 0 to Accounts.Count-1 do begin if Terminated then break; ProgressPos:=Acc; Synchronize(ProgressBar); Login:=copy(Accounts[Acc], 1, pos(del, Accounts[Acc])-1); Password:=copy(Accounts[Acc], pos(del, Accounts[Acc])+1, MaxInt); HTML:=IdHTTP.Get('http://xxxxxx'+Login+'xxxxxx'+Password); if pos ('xxxxxx', HTML)<>0 then if Form2.sEdit3.Text<>'' then begin HTML:=IdHTTP.Get('https://xxxxxx'+Form2.sEdit3.Text+'xxxxxx'); if pos ('xxxxxx', HTML)<>0 then begin Synchronize(Bad); end else begin Synchronize(Good); end end else begin Synchronize(Good); end else begin Synchronize(Bad); end; Synchronize(WriteFile); end; IdHTTP.Free; IdSSL.Free; end; Последний раз редактировалось AlexBerg001, 25.04.2015 в 12:09. |
#11
|
|||
|
|||
![]() В общем вопрос в том как сделать так, чтобы каждый новый поток брал следующую строку из Accounts
|
#12
|
||||
|
||||
![]() если использовать асинхронные (не блокируемые) сокеты, то потоки вообще не нужны.
Пишу программы за еду. __________________ |
#13
|
|||
|
|||
![]() Можно поподробнее немного? Что-то я не понял. Немного почитал про сокеты, но не так чтобы сильно понял. Понял только, что нужно как-то режим переключить, а как это можно сделать?
Последний раз редактировалось AlexBerg001, 25.04.2015 в 21:40. |
#14
|
|||
|
|||
![]() Мне кто нибудь подскажет, как переделать проект так чтобы потоки не обрабатывали одинаковые строки в Accounts или я тут сам с собой переписываюсь???
|
#15
|
||||
|
||||
![]() очевидно каждому потоку надо передавать свой набор исх. данных.
Пишу программы за еду. __________________ |