powered by simpleCommunicator - 2.0.61     © 2026 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / Delphi [игнор отключен] [закрыт для гостей] / Шаблон класса для работы с потоком (WThread, Thread)
25 сообщений из 469, страница 1 из 19
Шаблон класса для работы с потоком (WThread, Thread)
    #38411716
Фотография wadman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Собственно, сам шаблон с небольшими комментариями именно в тех местах, которые нужны на обывательском уровне, чтобы только добавить свой код.
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
136.
137.
138.
139.
140.
141.
142.
143.
144.
145.
146.
147.
148.
149.
150.
151.
152.
153.
154.
155.
156.
157.
158.
159.
160.
161.
162.
163.
164.
165.
166.
167.
168.
169.
170.
171.
172.
173.
174.
175.
176.
177.
178.
179.
180.
181.
182.
183.
184.
185.
186.
187.
188.
189.
190.
191.
192.
193.
194.
195.
196.
197.
198.
199.
200.
201.
202.
203.
204.
205.
206.
207.
208.
209.
210.
211.
212.
213.
214.
215.
216.
217.
218.
219.
220.
221.
222.
223.
224.
225.
226.
227.
228.
229.
230.
unit WThread;

interface

uses
    Classes,
    Windows,
    Messages,
    SysUtils;

type
    TWThread = class;

    // событие на получение строки из потока
    TWThreadReceiveString = procedure(Sender: TWThread; const Text: string) of object;
    // событие на получение числа из потока
    TWThreadReceiveInt = procedure(Sender: TWThread; const Int: Integer) of object;
    // событие на получение строки и числа из потока
    TWThreadReceiveStringWord = procedure(Sender: TWThread; const Text: string; const Wrd: Word) of object;
    // событие на получение двух чисел из потока
    TWThreadReceiveIntWord = procedure(Sender: TWThread; const Int: Integer; const Wrd: Word) of object;

    TWThread = class(TThread)
    private
        FToolWindow: THandle;
        hCloseEvent: THandle;
        FOnThreadReceviceString: TWThreadReceiveString;
        FOnThreadReceiveInt: TWThreadReceiveInt;
        FOnThreadReceiveStringWord: TWThreadReceiveStringWord;
        FOnThreadReceiveIntWord: TWThreadReceiveIntWord;
    protected
        procedure WWindowProc(var Msg: TMessage);
        procedure SendString(const Text: string);
        procedure SendInteger(const Int: Integer);
        procedure SendStringWord(const Text: string; const Wrd: Word);
        procedure SendIntegerWord(const Int: Integer; const Wrd: Word);
        procedure DoThreadReceiveString(const Text: string);
        procedure Execute; override; // шаблон процедуры
    public
        constructor Create(CreateSuspended: Boolean); virtual;
        destructor Destroy; override;
        // отправка любого сообщения в поток
        function SendMessageToThread(AMsg: UInt; wParam: WPARAM; lParam: LPARAM): BOOL;
        // отправка строки в поток
        function SendStringToThread(const Text: string): boolean;
        // остановка потока по феншую
        function StopThread: boolean;
        // события на получение данных из потока
        property OnThreadReceviceString: TWThreadReceiveString read FOnThreadReceviceString write FOnThreadReceviceString;
        property OnThreadReceiveInt: TWThreadReceiveInt read FOnThreadReceiveInt write FOnThreadReceiveInt;
        property OnThreadReceiveStringWord: TWThreadReceiveStringWord read FOnThreadReceiveStringWord write FOnThreadReceiveStringWord;
        property OnThreadReceiveIntWord: TWThreadReceiveIntWord read FOnThreadReceiveIntWord write FOnThreadReceiveIntWord;
    end;

implementation

const
    WM_BASE             = WM_USER + $100;
    WM_STRING           = WM_BASE + 1;
    WM_INT              = WM_BASE + 2;
    WM_STRING_WORD      = WM_BASE + 3;
    WM_INT_WORD         = WM_BASE + 4;

    WM_MAX              = WM_INT_WORD;

{ TWThread }

constructor TWThread.Create(CreateSuspended: Boolean);
begin
    inherited;
    FToolWindow := AllocateHWnd(WWindowProc);
    hCloseEvent := CreateEvent(nil, true, false, nil);
end;

destructor TWThread.Destroy;
begin
    CloseHandle(hCloseEvent);
    DeallocateHWnd(FToolWindow);
    inherited;
end;

procedure TWThread.DoThreadReceiveString(const Text: string);
begin
    if Assigned(FOnThreadReceviceString) then
        FOnThreadReceviceString(Self, Text);
end;

function TWThread.SendMessageToThread(AMsg: UInt; wParam: WPARAM; lParam: LPARAM): BOOL;
begin
    result := PostThreadMessage(ThreadID, AMsg, wParam, lParam);
end;

function TWThread.SendStringToThread(const Text: string): boolean;
var buf: LPARAM;
begin
    buf := 0;
    String(Buf) := Text;
    result := PostThreadMessage(ThreadID, WM_STRING, 0, buf);
end;

procedure TWThread.Execute;
var
    HandlesToWaitFor: array [0 .. 0] of THandle;
    dwHandleSignaled: DWORD;
    msg: TMsg;
    str: string;
label
    EndOfThread;
begin
    // следующая строка должна быть всегда первой в процедуре, т.к. запускает очередь сообщений для потока
    PeekMessage(msg, 0, WM_USER, WM_USER, PM_NOREMOVE);
    HandlesToWaitFor[0] := hCloseEvent;
    // SleepEx не нужен, если подготовительный код до цикла while будет достаточно долго выполняться
    // уточняется опытным путем, иначе очередь сообщений потока не успеет запуститься и сообщения могут улететь в пустоту
    SleepEx(20, false);

    // тестовый пример vvv
    SendString('WThread started');
    // тестовый пример ^^^

    while not Terminated do begin
        if not PeekMessage(msg, 0, WM_BASE, WM_MAX, PM_REMOVE) then begin
            dwHandleSignaled := MsgWaitForMultipleObjects(1, HandlesToWaitFor, false, INFINITE, QS_ALLINPUT);
            case dwHandleSignaled of
                WAIT_OBJECT_0: begin // выставлено событие остановки потока
                    Terminate;
                    goto EndOfThread;
                end;
                WAIT_OBJECT_0 + 1: begin // получено сообщение
                    Continue;
                end;
            end;
        end;
        if msg.hwnd <> 0 then begin
            TranslateMessage(msg);
            DispatchMessage(msg);
            Continue;
        end;
        case msg.message of
            WM_QUIT, WM_CLOSE, WM_DESTROY: begin // оконные сообщения тоже получаем
                Terminate;
                goto EndOfThread;
            end;
            WM_STRING: begin
                LPARAM(str) := msg.lParam;
                // что-нибудь делаем со строкой. например, отправляем обратно

                // тестовый пример vvv
                SendString(Format('received: %s', [str]));
                // тестовый пример ^^^

                str := ''; // "отпускаем" строку после работы с ней
            end;
            WM_INT: begin
                // здесь получаем число в msg.lparam;
            end;
        end;
    end;
EndOfThread:
    // все что создали в начале процедуры, здесь удаляем
end;

procedure TWThread.SendInteger(const Int: Integer);
begin
    if FToolWindow <> 0 then
        PostMessage(FToolWindow, WM_INT, 0, Int);
end;

procedure TWThread.SendIntegerWord(const Int: Integer; const Wrd: Word);
begin
    if FToolWindow <> 0 then
        PostMessage(FToolWindow, WM_INT_WORD, Wrd, Int);
end;

procedure TWThread.SendString(const Text: string);
var buf: LParam;
begin
    buf := 0;
    String(buf) := Text;
    if FToolWindow <> 0 then
        PostMessage(FToolWindow, WM_STRING, 0, buf);
end;

procedure TWThread.SendStringWord(const Text: string; const Wrd: Word);
var buf: LParam;
begin
    buf := 0;
    String(buf) := Text;
    if FToolWindow <> 0 then
        PostMessage(FToolWindow, WM_STRING, Wrd, buf);
end;

function TWThread.StopThread: boolean;
begin
    SetEvent(hCloseEvent);
    SleepEx(1, false);
    result := WaitForSingleObject(Handle, 10000) <> WAIT_TIMEOUT;
end;

procedure TWThread.WWindowProc(var Msg: TMessage);
var str: String;
begin
    case Msg.Msg of
        WM_STRING: begin
            LPARAM(str) := Msg.LParam;
            DoThreadReceiveString(str);
            Msg.Result := 1;
        end;
        WM_INT: begin
            if Assigned(FOnThreadReceiveInt) then
                FOnThreadReceiveInt(Self, Msg.LParam);
            Msg.Result := 1;
        end;
        WM_STRING_WORD: begin
            LPARAM(str) := Msg.LParam;
            if Assigned(FOnThreadReceiveStringWord) then
                FOnThreadReceiveStringWord(Self, str, Msg.WParam);
            Msg.Result := 1;
        end;
        WM_INT_WORD: begin
            if Assigned(FOnThreadReceiveIntWord) then
                FOnThreadReceiveIntWord(Self, Msg.LParam, Msg.WParam);
            Msg.Result := 1;
        end;
    else
        Msg.Result := DefWindowProc(FToolWindow, Msg.Msg, Msg.wParam, Msg.lParam);
    end;
end;

end.



Тестовый проект. Форма состоит из мemo, и двух кнопок: одна запускает/останавливает поток, другая отправляет строку в поток.
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
unit ThreadTestMain;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, WThread;

type
  TfrmWThreadTest = class(TForm)
    Memo1: TMemo;
    Button1: TButton;
    Button2: TButton;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    FThread: TWThread;
    procedure ReceiveString(Sender: TWThread; const Text: string);
    procedure StartThread;
    procedure StopThread;
  public
  end;

var
  frmWThreadTest: TfrmWThreadTest;

implementation

{$R *.dfm}

procedure TfrmWThreadTest.Button1Click(Sender: TObject);
begin
    if Assigned(FThread) then
        StopThread
    else
        StartThread;
end;

procedure TfrmWThreadTest.Button2Click(Sender: TObject);
var s: string;
begin
    if Assigned(FThread) and InputQuery('Input', 'Enter text', s) then begin
        Memo1.Lines.Add(Format('-> %s', [s]));
        FThread.SendStringToThread(s);
    end;
end;

procedure TfrmWThreadTest.FormClose(Sender: TObject; var Action: TCloseAction);
begin
    if Assigned(FThread) then
        StopThread;
end;

procedure TfrmWThreadTest.FormCreate(Sender: TObject);
begin
    Memo1.Lines.Clear;
end;

procedure TfrmWThreadTest.ReceiveString(Sender: TWThread; const Text: string);
begin
    Memo1.Lines.Add(Format('<- %s', [Text]));
end;

procedure TfrmWThreadTest.StartThread;
begin
    if not Assigned(FThread) then begin
        FThread := TWThread.Create(true);
        FThread.OnThreadReceviceString := ReceiveString;
        FThread.Start;
    end;
end;

procedure TfrmWThreadTest.StopThread;
begin
    if Assigned(FThread) then begin
        if not FThread.StopThread then
            MessageDlg('You dont kill me!', mtError, [mbOk], 0);
        FThread.Free;
        FThread := nil;
    end;
end;

end.



Тестирование было с ReportMemoryLeaksOnShutdown := true; что показало, что память не теряется при многочисленных отправках строк. Delphi XE2.

Вопросы, предложения? :)

Репозитарий на гитхабе: https://github.com/wadman/wthread/
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38411889
fd00ch
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
wadman,

1. KISS
2. RTFM твой WM_STRING - это PSM_SHEETINFO. маловероятно, конечно, что система догадается посласть его твоему окну, но когда догадается таки - ожидаемой строки ты там не найдешь))
3. зачем отправлять всего лишь Word, когда "по ширине" вполне пролазит WPARAM?
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38411901
Фотография wadman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
fd00ch KISS
Не осилил, поясни. Если речь о Terminate и goto, то считай это перестраховкой для начинающего.
fd00chтвой WM_STRING - это PSM_SHEETINFO.
WM_USER + $100 + 1 <> WM_USER+101
fd00chзачем отправлять всего лишь Word, когда "по ширине" вполне пролазит WPARAM?
Можно и так.
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38411938
fd00ch
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
wadmanWM_USER + $100 + 1 <> WM_USER+101профнепригодность, блин, доллары перестаю замечать

wadmanНе осилил, пояснизачем писать сложно, когда можно просто? у тебя задача потока в чем заключается?
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38411944
Фотография wadman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
fd00chу тебя задача потока в чем заключается?
У меня - ни в чем. Свистоперделка Конкретный вопрос задай.
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38411965
fd00ch
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
1. зачем нужен такой поток?
2. зачем потоку нужны сообщающие о принятых данных события, которые не синхронизированы ни с чем?
3. по мотивам п.2. в чьем контексте вызывается TfrmWThreadTest.ReceiveString с обращением к Memo1?
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38411971
Aleksey V.P.
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
wadman,

Не понял для чего шаблон потока. Сделай пояснения, please.
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38411973
Фотография wadman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
fd00ch1. зачем нужен такой поток?
2. зачем потоку нужны сообщающие о принятых данных события, которые не синхронизированы ни с чем?
Это шаблон.
fd00chв чьем контексте вызывается TfrmWThreadTest.ReceiveString с обращением к Memo1?
Естественно в главном потоке, который обслуживает VCL. Как и все, что дергается из WWindowProc.
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38411978
Фотография wadman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38411988
Фотография defecator
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Модератор форума
Я вижу в тексте слово GOTO - код КГ/АМ
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38411992
Фотография wadman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
defecatorЯ вижу в тексте слово GOTO - код КГ/АМ
Перед этим Label не заметил?
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38411997
Aleksey V.P.
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
wadmanДля людей, которые создают такие темы:
http://www.sql.ru/forum/1050309/tthread-nuzhna-li-v-privedennom-kode-sinhronizaciya
http://www.sql.ru/forum/1050203/obrashhenie-k-forme-iz-thread
http://www.sql.ru/forum/1049953/osnovy-tthread

Спасиба за пояснения, буэшечку словил. Поясни смысл использования оператора GoTo, без него никак не обойтись?
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38412005
Aleksey V.P.
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
wadman,

Например использовать try .. finally .. end..
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38412007
Фотография wadman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Aleksey V.P.без него никак не обойтись?
Обойтись, тот же break. Только я не сторонник бинарной логики "если, то", то есть если выполняется, как заложено, то нехай буде.
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38412010
Фотография wadman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Aleksey V.P.Например использовать try .. finally .. end..
В этом коде это лишнее. Раз уж такой любитель процедурных вызовов, то try должны расставлены быть в вызываемых процедурах.
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38412012
fd00ch
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
wadmanЕстественно в главном потоке, который обслуживает VCL. Как и все, что дергается из WWindowProc.да, прощелкал, что создание окна идет из конструктора, а не из Execute
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38412043
Фотография wadman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
wadman http://www.sql.ru/forum/1050203/obrashhenie-k-forme-iz-thread
14899112 Пример на основе моего модуля:
1. Добавляется процедура своего парсера.
2. В Execute ловится любой свой код посредством SendMessageToThread и процедура запускается.
3. Из потока вполне себе можно отправлять сообщения: прогресс выполнения, коды действия, диагностические сообщения и т.п.

Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
136.
137.
138.
139.
140.
141.
142.
143.
144.
145.
146.
147.
148.
149.
150.
151.
152.
153.
154.
155.
156.
157.
158.
159.
160.
161.
162.
163.
164.
165.
166.
167.
168.
169.
170.
171.
172.
173.
174.
175.
176.
177.
178.
179.
180.
181.
182.
183.
184.
185.
186.
187.
188.
189.
190.
191.
192.
193.
194.
195.
196.
197.
198.
199.
200.
201.
202.
203.
204.
205.
206.
207.
208.
209.
210.
211.
212.
213.
214.
215.
216.
217.
218.
219.
220.
221.
222.
223.
224.
225.
226.
227.
228.
229.
230.
231.
232.
233.
234.
235.
236.
237.
238.
239.
240.
241.
242.
243.
244.
245.
246.
247.
248.
249.
250.
251.
252.
253.
254.
255.
256.
257.
258.
259.
260.
unit WThread;

interface

uses
    Classes,
    Windows,
    Messages,
    SysUtils;

type
    TWThread = class;

    // событие на получение строки из потока
    TWThreadReceiveString = procedure(Sender: TWThread; const Text: string) of object;
    // событие на получение числа из потока
    TWThreadReceiveInt = procedure(Sender: TWThread; const Int: Integer) of object;
    // событие на получение строки и числа из потока
    TWThreadReceiveStringWord = procedure(Sender: TWThread; const Text: string; const DWrd: Word) of object;
    // событие на получение двух чисел из потока
    TWThreadReceiveIntWord = procedure(Sender: TWThread; const Int: Integer; const DWrd: Word) of object;

    TWThread = class(TThread)
    private
        FToolWindow: THandle;
        hCloseEvent: THandle;
        FOnThreadReceviceString: TWThreadReceiveString;
        FOnThreadReceiveInt: TWThreadReceiveInt;
        FOnThreadReceiveStringWord: TWThreadReceiveStringWord;
        FOnThreadReceiveIntWord: TWThreadReceiveIntWord;
    protected
        procedure WWindowProc(var Msg: TMessage);
        procedure SendString(const Text: string);
        procedure SendInteger(const Int: Integer);
        procedure SendStringWord(const Text: string; const Wrd: DWord);
        procedure SendIntegerWord(const Int: Integer; const Wrd: DWord);
        procedure DoThreadReceiveString(const Text: string);
        procedure ParseUrl(const URL: string); // процедура парсера
        procedure Execute; override; // шаблон процедуры
    public
        constructor Create(CreateSuspended: Boolean); virtual;
        destructor Destroy; override;
        // отправка любого сообщения в поток
        function SendMessageToThread(AMsg: UInt; wParam: WPARAM; lParam: LPARAM): BOOL;
        // отправка строки в поток
        function SendStringToThread(const Text: string): boolean;
        // отправка числа и строки в поток
        function SendStringIntToThread(const Text: string; const Wrd: DWord): boolean;
        // остановка потока по феншую
        function StopThread: boolean;
        // события на получение данных из потока
        property OnThreadReceviceString: TWThreadReceiveString read FOnThreadReceviceString write FOnThreadReceviceString;
        property OnThreadReceiveInt: TWThreadReceiveInt read FOnThreadReceiveInt write FOnThreadReceiveInt;
        property OnThreadReceiveStringWord: TWThreadReceiveStringWord read FOnThreadReceiveStringWord write FOnThreadReceiveStringWord;
        property OnThreadReceiveIntWord: TWThreadReceiveIntWord read FOnThreadReceiveIntWord write FOnThreadReceiveIntWord;
    end;

implementation

const
    WM_BASE             = WM_USER + $100;
    WM_STRING           = WM_BASE + 1;
    WM_INT              = WM_BASE + 2;
    WM_STRING_WORD      = WM_BASE + 3;
    WM_INT_WORD         = WM_BASE + 4;

    WM_MAX              = WM_INT_WORD;

{ TWThread }

constructor TWThread.Create(CreateSuspended: Boolean);
begin
    inherited;
    FToolWindow := AllocateHWnd(WWindowProc);
    hCloseEvent := CreateEvent(nil, true, false, nil);
end;

destructor TWThread.Destroy;
begin
    CloseHandle(hCloseEvent);
    DeallocateHWnd(FToolWindow);
    inherited;
end;

procedure TWThread.DoThreadReceiveString(const Text: string);
begin
    if Assigned(FOnThreadReceviceString) then
        FOnThreadReceviceString(Self, Text);
end;

function TWThread.SendMessageToThread(AMsg: UInt; wParam: WPARAM; lParam: LPARAM): BOOL;
begin
    result := PostThreadMessage(ThreadID, AMsg, wParam, lParam);
end;

function TWThread.SendStringToThread(const Text: string): boolean;
var buf: LPARAM;
begin
    buf := 0;
    String(Buf) := Text;
    result := PostThreadMessage(ThreadID, WM_STRING, 0, buf);
end;

function TWThread.SendStringIntToThread(const Text: string; const Wrd: DWord): boolean;
var buf: LPARAM;
begin
    buf := 0;
    String(Buf) := Text;
    result := PostThreadMessage(ThreadID, WM_STRING_WORD, Wrd, buf);
end;

procedure TWThread.Execute;
var
    HandlesToWaitFor: array [0 .. 0] of THandle;
    dwHandleSignaled: DWORD;
    msg: TMsg;
    str: string;
    dw: DWord;
label
    EndOfThread;
begin
    // следующая строка должна быть всегда первой в процедуре, т.к. запускает очередь сообщений для потока
    PeekMessage(msg, 0, WM_USER, WM_USER, PM_NOREMOVE);
    HandlesToWaitFor[0] := hCloseEvent;
    // SleepEx не нужен, если подготовительный код до цикла while будет достаточно долго выполняться
    // уточняется опытным путем, иначе очередь сообщений потока не успеет запуститься и сообщения могут улететь в пустоту
    SleepEx(20, false);

    // тестовый пример vvv
    SendString('WThread started');
    // тестовый пример ^^^

    while not Terminated do begin
        if not PeekMessage(msg, 0, WM_BASE, WM_MAX, PM_REMOVE) then begin
            dwHandleSignaled := MsgWaitForMultipleObjects(1, HandlesToWaitFor, false, INFINITE, QS_ALLINPUT);
            case dwHandleSignaled of
                WAIT_OBJECT_0: begin // выставлено событие остановки потока
                    Terminate;
                    goto EndOfThread;
                end;
                WAIT_OBJECT_0 + 1: begin // получено сообщение
                    Continue;
                end;
            end;
        end;
        if msg.hwnd <> 0 then begin
            TranslateMessage(msg);
            DispatchMessage(msg);
            Continue;
        end;
        case msg.message of
            WM_QUIT, WM_CLOSE, WM_DESTROY: begin // оконные сообщения тоже получаем
                Terminate;
                goto EndOfThread;
            end;
            WM_STRING: begin
                LPARAM(str) := msg.lParam;
                // что-нибудь делаем со строкой. например, отправляем обратно

                // тестовый пример vvv
                SendString(Format('received: %s', [str]));
                // тестовый пример ^^^

                str := ''; // "отпускаем" строку после работы с ней
            end;
            WM_INT: begin
                // здесь получаем число в msg.lparam;
            end;
            WM_STRING_WORD: begin
                dw := msg.wParam;
                LPARAM(str) := msg.lParam;
                // что-нибудь делаем со строкой и числом.
                // например число было 77
                case dw of
                    77: ParseUrl(str);
                end;

                str := ''; // "отпускаем" строку после работы с ней
            end;
        end;
    end;
EndOfThread:
    // все что создали в начале процедуры, здесь удаляем
end;

procedure TWThread.ParseUrl(const URL: string);
begin
    SendString('Begin parsing...');
    // парсер
    SendString('Done parsing...');
end;

procedure TWThread.SendInteger(const Int: Integer);
begin
    if FToolWindow <> 0 then
        PostMessage(FToolWindow, WM_INT, 0, Int);
end;

procedure TWThread.SendIntegerWord(const Int: Integer; const Wrd: DWord);
begin
    if FToolWindow <> 0 then
        PostMessage(FToolWindow, WM_INT_WORD, Wrd, Int);
end;

procedure TWThread.SendString(const Text: string);
var buf: LParam;
begin
    buf := 0;
    String(buf) := Text;
    if FToolWindow <> 0 then
        PostMessage(FToolWindow, WM_STRING, 0, buf);
end;

procedure TWThread.SendStringWord(const Text: string; const Wrd: DWord);
var buf: LParam;
begin
    buf := 0;
    String(buf) := Text;
    if FToolWindow <> 0 then
        PostMessage(FToolWindow, WM_STRING, Wrd, buf);
end;

function TWThread.StopThread: boolean;
begin
    SetEvent(hCloseEvent);
    SleepEx(1, false);
    result := WaitForSingleObject(Handle, 10000) <> WAIT_TIMEOUT;
end;

procedure TWThread.WWindowProc(var Msg: TMessage);
var str: String;
begin
    case Msg.Msg of
        WM_STRING: begin
            LPARAM(str) := Msg.LParam;
            DoThreadReceiveString(str);
            Msg.Result := 1;
        end;
        WM_INT: begin
            if Assigned(FOnThreadReceiveInt) then
                FOnThreadReceiveInt(Self, Msg.LParam);
            Msg.Result := 1;
        end;
        WM_STRING_WORD: begin
            LPARAM(str) := Msg.LParam;
            if Assigned(FOnThreadReceiveStringWord) then
                FOnThreadReceiveStringWord(Self, str, Msg.WParam);
            Msg.Result := 1;
        end;
        WM_INT_WORD: begin
            if Assigned(FOnThreadReceiveIntWord) then
                FOnThreadReceiveIntWord(Self, Msg.LParam, Msg.WParam);
            Msg.Result := 1;
        end;
    else
        Msg.Result := DefWindowProc(FToolWindow, Msg.Msg, Msg.wParam, Msg.lParam);
    end;
end;

end.



То есть поток выполняется всегда, но когда не парсит, то ожидает "приказа" в виде SendStringIntToThread(77, 'http://ya.ru')
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38412112
Фотография Dimonka
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
wadman,

Блин, как-то всё необоснованно сложно завернуто.
Нет никакого стека для присланных URL, если поток не пропарсил предыдущий URL, то передать следующий URL будет проблематично. Особенно если это связано с большими ресурсными затратами или с обращением к устройствам ввода/вывода.

Предлагаю изменить для теста код твоего потока следующим образом:
procedure TWThread.ParseUrl(const URL: string);
begin
SendString('Begin parsing...');
// парсер
Sleep(1000);
SendString('Done parsing...');
end;

и попробовать пообращаться к потоку раз в пол секунды с разными урлами. Сколько урлов пропарсит твой поток?
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38412125
Фотография wadman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
DimonkaНет никакого стека для присланных URL
Откуда такая информация?
DimonkaСколько урлов пропарсит твой поток?
Столько, сколько вмещает очередь сообщений ОС.
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38412139
Фотография wadman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Но именно в случае парсером страниц я бы сделал не один, а 50 потоков, регулируемых из главного, а не пихал бы всю работу в один доп поток. И при этом анализировал бы занятость потоков. Благо для этого (диалога между основным и доп потоком) тут все предусмотрено.
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38412228
Фотография wadman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Завтра напишу чуть более сложный пример для 50-ти потоков, которые стартуют случайно, отсчитывают случайное количество секунд и сообщают о своей работу основному потоку. Никаких тормозов интерфейса при этом не будет. Примерно такой метод работы с устройствами у меня используется на производстве.
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38412263
Фотография Dimonka
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
wadmanDimonkaНет никакого стека для присланных URL
Откуда такая информация?
DimonkaСколько урлов пропарсит твой поток?
Столько, сколько вмещает очередь сообщений ОС.
Ну так вот в том-то и дело, что поток использует очередь сообщений, а не очередь заданий. Отправителю надо будет хранить урлы, пока их не обработает поток.
Напиши код по обработке 2х урлов применро так:
Код: pascal
1.
2.
3.
4.
5.
s := "123";
Thread.DoParseURL(s);
s := "321";
Thread.DoParseURL(s);
s := "";

и посмотри, что он тебе выдаст.

Только не забудь поставить sleep(20) в парсинг урлов для изображения непосильного труда.

зы
DoParseURL() должен посылать какие надо команды для парсинга.
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38412284
Фотография wadman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Dimonka, сам напиши DoParseUrl и покажи, что в нем не так. У меня впечатление, что ты не понимаешь до конца, как это работает.
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38412290
Гаджимурадов Рустам
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
2 wadman

Не вчитывался ни в класс, ни в шаблон, ни в ваш диалог,
но на беглый взгляд - я бы ни в коем, НИ В КОЕМ случае
не использовал сообщения/события для обмена данными,
только для сигнализации. Тем более в шаблоне потока.

Posted via ActualForum NNTP Server 1.5
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38412293
Фотография Dimonka
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
wadman,

я так полагаю, это будет что-то вроде этого?

Код: pascal
1.
2.
3.
4.
5.
  
procedure TWThread.DoParseURL(const URL: string);
begin
  SendStringWord(URL, 77);
end;



Можешь тестировать :)
...
Рейтинг: 0 / 0
25 сообщений из 469, страница 1 из 19
Форумы / Delphi [игнор отключен] [закрыт для гостей] / Шаблон класса для работы с потоком (WThread, Thread)
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]