![]() |
|
|
|||||||
| Регистрация | << Правила форума >> | 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
|
||||
|
||||
|
очевидно каждому потоку надо передавать свой набор исх. данных.
|