powered by simpleCommunicator - 2.0.61     © 2026 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / Delphi [игнор отключен] [закрыт для гостей] / Шаблон класса для работы с потоком (WThread, Thread)
25 сообщений из 469, страница 4 из 19
Шаблон класса для работы с потоком (WThread, Thread)
    #38425139
Фотография Док
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
wadmanпочему, хэлп ответит
т.е. у тебя еще и шкатулка с секретом?

Тогда нафиг, сам себе напишу
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38430776
Фотография 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.
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.
261.
262.
263.
264.
265.
266.
unit WThread;
// модуль для работы с доп.потоками
// позволяет "общаться" дополнительному и основному потокам посредством системной очереди сообщений
// без вызова синхронизации
// (c) wadman 2013

// использование:
// 1. Создать наследника с объявленными обработчиками сообщений между WM_THREAD_BASE и WM_THREAD_MAX
//     пример: procedure WMTestProc(var Msg: TMessage); message WM_TEST_PROC;
// 2. Присвоить обработчика(ов) события OnThreadReceiveMessage, OnTimeOut - при необходимости
// Для обмена строками рекомендууется использовать функции NewString и FreeString

interface

uses
    Classes,
    Windows,
    Messages,
    SysUtils;

const
    WM_THREAD_BASE      = WM_USER + $110;
    WM_THREAD_MAX       = WM_USER + $7FFF;

type
    TWThread = class;

    // событие на получение сообщения из потока
    TWThreadReceiveMessage = procedure(Sender: TWThread; var Msg: Cardinal; var WParam: WPARAM; var LParam: LPARAM) of object;
    // событие из потока на таймаут
    TWThreadTimeOut = procedure(Sender: TWThread) of object;

    TWThread = class(TThread)
    private
        FToolWindow: THandle;
        hCloseEvent: THandle;
        FOnThreadReceiveMessage: TWThreadReceiveMessage;
        FTimeOut: Cardinal;
        FOnTimeOut: TWThreadTimeOut;
        FTimeOutIsDirect: boolean;
        procedure SetTimeOut(const Value: Cardinal);
    protected
        procedure WWindowProc(var Msg: TMessage);
        // отправка сообщения из этого потока для вызова обрабочика OnThreadReceiveMessage
        procedure SendMessageFromMe(const Msg: Cardinal; const WParam: WPARAM; const LParam: LPARAM);
        procedure DoThreadReceiveMessage(var Msg: Cardinal; var WParam: WPARAM; var LParam: LPARAM);
        procedure DoTimeout;
        procedure Execute; override;
    public
        constructor Create; overload;
        constructor Create(CreateSuspended: Boolean); overload;
        destructor Destroy; override;
        // см TimeOutIsDirect, при true - перекрыть следующую процедуру
        procedure DirectTimeOut; virtual;
        // отправка любого сообщения В этот поток
        function SendToThreadMessage(AMsg: UInt; wParam: WPARAM; lParam: LPARAM): BOOL;
        // остановка потока по феншую
        function StopThread: boolean;
        // событие на получение данных ИЗ этого потока, вызывается в основном потоке
        property OnThreadReceiveMessage: TWThreadReceiveMessage read FOnThreadReceiveMessage write FOnThreadReceiveMessage;
        // событие, возникающее при превышении интервала ожидания
        property OnTimeOut: TWThreadTimeOut read FOnTimeOut write FOnTimeOut;
        // интервал ожидания в мс, по-умолчанию - бесконечно
        property TimeOut: Cardinal read FTimeOut write SetTimeOut default INFINITE;
        // true = Будет вызван DirectTimeOut из этого потока, false = вызвано событие OnTimeOut в основном потоке.
        property TimeOutIsDirect: boolean read FTimeOutIsDirect write FTimeOutIsDirect default false;
    end;

// подготовка строки к обмену между потоками
function NewString(const Text: string): LPARAM;

// возвращение строки к привывчному виду после приема из другого потока
function FreeString(var P: LPARAM): String;

implementation

const
    WM_MSG              = WM_USER+$100;
    WM_TIMEOUT          = WM_USER+$101;

    SizeOfChar          = SizeOf(Char);

function NewString(const Text: string): LPARAM;
begin
    Result := 0;
    string(Result) := Text;
end;

function FreeString(var P: LPARAM): String;
begin
    LPARAM(Result) := P;
end;

{function FreeString(var P: LPARAM): String;
begin
    if LongBool(P) then begin
        SetLength(Result, Length(PChar(P)));
        Move(Pointer(P)^, Pointer(Result)^, Length(Result)*SizeOfChar);
        P := LocalFree(HLOCAL(P));
    end;
end;

function NewString(const Text: string): LPARAM;
var l: Integer;
begin
    l := Length(Text)*SizeOfChar;
    if L > 0 then begin
        Result := LocalAlloc(LPTR, l+SizeOfChar);
        if LongBool(Result) then
            Move(Pointer(Text)^, Pointer(Result)^, l);
    end else
        Result := 0;
end;    }

{ TWThread }

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

constructor TWThread.Create;
begin
    Create(False);
end;

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

procedure TWThread.DirectTimeOut;
begin
    // override
end;

procedure TWThread.DoThreadReceiveMessage(var Msg: Cardinal; var WParam: WPARAM; var LParam: LPARAM);
begin
    if Assigned(FOnThreadReceiveMessage) then
        FOnThreadReceiveMessage(Self, Msg, WParam, LParam);
end;

procedure TWThread.DoTimeout;
begin
    if Assigned(FOnTimeOut) then
        FOnTimeOut(Self);
end;

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

procedure TWThread.SetTimeOut(const Value: Cardinal);
begin
    if FTimeOut <> Value then begin
        FTimeOut := Value;
        if not Suspended then
            PostThreadMessage(ThreadID, WM_TIMEOUT, Value, 0);
    end;
end;

procedure TWThread.Execute;
var
    HandlesToWaitFor: array [0 .. 0] of THandle;
    dwHandleSignaled: DWORD;
    msg: TMsg;
    message: TMessage;

    MSWait: Cardinal;
Label
    EndOfThread;
begin
    // следующая строка должна быть всегда первой в процедуре, т.к. запускает очередь сообщений для потока
    PeekMessage(msg, 0, WM_USER, WM_USER, PM_NOREMOVE);
    HandlesToWaitFor[0] := hCloseEvent;
    // SleepEx не нужен, если подготовительный код до цикла while будет достаточно долго выполняться
    // уточняется опытным путем, иначе очередь сообщений потока не успеет запуститься и сообщения могут улететь в пустоту
    //SleepEx(20, true);

    MSWait := FTimeOut; // изменяя кол-во миллисекунд в MSWait можно организовать событие-таймер, см WAIT_TIMEOUT
                        // в данном случае установлено бесконечное ожидание любого события

    while not Terminated do begin
        if not PeekMessage(msg, 0, WM_THREAD_BASE, WM_THREAD_MAX, PM_REMOVE) then begin
            dwHandleSignaled := MsgWaitForMultipleObjects(1, HandlesToWaitFor, false, MSWait, QS_ALLINPUT);
            case dwHandleSignaled of
                WAIT_OBJECT_0: begin // выставлено событие остановки потока
                    if not FreeOnTerminate then
                        Terminate;
                    goto EndOfThread;
                end;
                WAIT_OBJECT_0 + 1: begin // получено сообщение
                    Continue;
                end;
                WAIT_TIMEOUT: begin
                    if FTimeOutIsDirect then
                        DirectTimeOut
                    else
                        SendMessageFromMe(WM_TIMEOUT, 0, 0);
                    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 // оконные сообщения тоже получаем
                if not FreeOnTerminate then
                    Terminate;
                goto EndOfThread;
            end;
            WM_TIMEOUT: begin
                MSWait := msg.wParam;
            end
            else begin
                message.Msg := Msg.message;
                message.WParam := Msg.wParam;
                message.LParam := Msg.lParam;
                Dispatch(message);
            end;
        end;
    end;
EndOfThread:
    // все что создали в начале процедуры, здесь удаляем
end;

procedure TWThread.SendMessageFromMe(const Msg: Cardinal; const WParam: WPARAM; const LParam: LPARAM);
begin
    if FToolWindow <> 0 then
        PostMessage(FToolWindow, Msg, WParam, LParam);
end;

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

procedure TWThread.WWindowProc(var Msg: TMessage);
begin
    case Msg.Msg of
        WM_TIMEOUT: begin
            DoTimeout;
            Msg.Result := 1;
        end;
        WM_THREAD_BASE..WM_USER+WM_THREAD_MAX: begin
            DoThreadReceiveMessage(Msg.Msg, Msg.WParam, Msg.LParam);
            Msg.Result := 1;
        end
    else
        Msg.Result := DefWindowProc(FToolWindow, Msg.Msg, Msg.wParam, Msg.lParam);
    end;
end;

end.



Пример использования:
Код: 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.
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;

const
    WM_STRING   = WM_THREAD_BASE + 1;

type
    TMyThread = class(TWThread)
    private
        procedure ReceiveString(var Msg: TMessage); message WM_STRING;
    end;

  TfrmWThreadTest = class(TForm)
    Memo1: TMemo;
    Button1: TButton;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    FThread: TMyThread;
    procedure ReceiveMsg(Sender: TWThread; var Msg: Cardinal; var WParam: WPARAM; var LParam: LPARAM);
    procedure StartThread;
    procedure StopThread;
  public
  end;

var
  frmWThreadTest: TfrmWThreadTest;

implementation

{$R *.dfm}

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

procedure TfrmWThreadTest.Button2Click(Sender: TObject);
var s: string;
begin
    if LongBool(FThread) and InputQuery('Input', 'Enter text', s) then begin
        // отправляем введенную строку в доп. поток
        Memo1.Lines.Add(Format('-> %s', [s]));
        FThread.SendToThreadMessage(WM_STRING, 0, NewString(s));
        s := '';
    end;
end;

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

procedure TfrmWThreadTest.ReceiveMsg(Sender: TWThread; var Msg: Cardinal; var WParam: WPARAM; var LParam: LPARAM);
begin
    case Msg of
        WM_STRING: begin
            Memo1.Lines.Add(Format('<- %s', [FreeString(LParam)]));
        end;
    end;
end;

procedure TfrmWThreadTest.StartThread;
begin
    if not LongBool(FThread) then begin
        FThread := TMyThread.Create(false);
        FThread.OnThreadReceiveMessage := ReceiveMsg;
    end;
end;

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

{ TMyThread }

procedure TMyThread.ReceiveString(var Msg: TMessage);
var s: string;
begin
    s := FreeString(Msg.LParam);
    // отправляем полученную строку обратно главному потоку
    SendMessageFromMe(Msg.Msg, Msg.WParam, NewString(s));
    sleep(5000);
    SendMessageFromMe(Msg.Msg, Msg.WParam, NewString(s));
end;

end.

...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38431255
fd00ch
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
wadman, а что произойдет с сообщениями, которые были отправлены ToolWindow в конце Execute, если след.командой будет вызов деструктора, где проиходит уничтожение этого самого ToolWindow?
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38431387
Фотография wadman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
fd00chа что произойдет с сообщениями
Улетят в пустоту. Так же как и те, которые будут отправлены в поток до создания очереди сообщений для него, т.к. очередь для потока создается с небольшой задержкой.
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38432107
Фотография Gator
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
wadman, Вы ChangeLog ведёте? Скрываете? :)
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38432271
Фотография wadman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Gatorwadman, Вы ChangeLog ведёте? Скрываете? :)
Таки нет. Впечатление, что такие мелочи не требуют соблюдения формальностей. На вопросы "что как и почему" отвечаю.
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38445448
Фотография wadman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Поколение Next: Delphi&Lazarus. В виду отсутствия некоторых приятностей в Лазарусе пришлось немного извернуться. Для коммуникации доп. потока с основным прикрутил еще один поток.

Модуль:
Код: 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.
261.
262.
263.
264.
265.
266.
267.
268.
269.
270.
271.
272.
273.
274.
275.
276.
277.
278.
279.
280.
281.
282.
283.
284.
285.
286.
287.
288.
289.
290.
291.
292.
293.
294.
295.
296.
297.
298.
299.
300.
301.
302.
303.
304.
305.
306.
307.
308.
309.
310.
311.
312.
313.
314.
315.
316.
317.
318.
319.
320.
321.
322.
323.
324.
325.
326.
327.
328.
329.
330.
331.
unit WThread;

// модуль для работы с доп.потоками Delphi&Lazarus
// позволяет "общаться" дополнительному и основному потокам посредством очереди сообщений
// (c) wadman 2013

// использование:
// 1. Создать наследника с объявленными обработчиками сообщений
//     пример: procedure WMTestProc(var Msg: TThreadMessage); message WM_TEST_PROC;
// 2. Присвоить обработчика(ов) события OnThreadReceiveMessage, OnTimeOut - при необходимости
// Для обмена строками рекомендууется использовать функции NewString и FreeString

{$IFDEF FPC}
{$mode objfpc}{$H+}
{$ENDIF}

interface

uses
    Classes, SyncObjs, Variants;

type
    PThreadMessage = ^TThreadMessage;
    TThreadMessage = record
        {$IFDEF FPC}
        Message: DWord;
        {$ELSE}
        Message: Word;
        {$ENDIF}
        WParam: Word;
        LParam: NativeInt;
    end;

    TWThread = class;

    TWThreadReceiveMessage = procedure(Sender: TWThread; Msg: TThreadMessage) of object;
    TWThreadTimeOut = procedure(Sender: TWThread) of object;

    { TWThread }

    TWThread = class(TThread)
    private
        FMessageEvent: TEvent;
        FOnThreadReceiveMessage: TWThreadReceiveMessage;
        FOnTimeOut: TWThreadTimeOut;
        FQueue: TList;
        FSection: TCriticalSection;
        FTimeOut: Cardinal;
        FTimeOutIsDirect: boolean;
        FGUIThread: TThread;
        procedure SetTimeOut(AValue: Cardinal);
        procedure DoTimeOut;
    protected
        procedure Execute; override;
        {$IFDEF FPC}
        procedure SendGUIMessage(Message: DWord; WParam: Word; LParam: NativeInt);
        {$ELSE}
        procedure SendGUIMessage(Message: Word; WParam: Word; LParam: NativeInt);
        {$ENDIF}
    public
        constructor Create; overload;
        constructor Create(CreateSuspended: Boolean); overload;
        destructor Destroy; override;
        procedure DirectTimeOut; virtual;
        {$IFDEF FPC}
        procedure PostMessage(Message: DWord; WParam: Word; LParam: NativeInt);
        {$ELSE}
        procedure PostMessage(Message: Word; WParam: Word; LParam: NativeInt);
        {$ENDIF}
        procedure StopThread;
        // событие на получение данных ИЗ этого потока, вызывается в основном потоке
        property OnThreadReceiveMessage: TWThreadReceiveMessage read FOnThreadReceiveMessage write FOnThreadReceiveMessage;
        // событие, возникающее при превышении интервала ожидания
        property OnTimeOut: TWThreadTimeOut read FOnTimeOut write FOnTimeOut;
        // интервал ожидания в мс, по-умолчанию - бесконечно
        property TimeOut: Cardinal read FTimeOut write SetTimeOut default INFINITE;
        // true = Будет вызван DirectTimeOut из этого потока, false = вызвано событие OnTimeOut в основном потоке.
        property TimeOutIsDirect: boolean read FTimeOutIsDirect write FTimeOutIsDirect default false;
    end;

// подготовка строки к обмену между потоками
function NewString(const Text: string): NativeInt;

// возвращение строки к привывчному виду после приема из другого потока
function FreeString(var P: NativeInt): String;

implementation

type

    { TGUIThread }

    TGUIThread = class(TThread)
    private
        FMessageEvent: TEvent;
        FTimeOut: Boolean;
        FOwner: TWThread;
        FQueue: TList;
        FSection: TCriticalSection;
        FCurrentMessage: TThreadMessage;
    protected
        procedure Execute; override;
        procedure CallGUIThread;
    public
        constructor Create(AOwner: TWThread); overload;
        destructor Destroy; override;
        {$IFDEF FPC}
        procedure PostMessage(Message: DWord; WParam: Word; LParam: NativeInt);
        {$ELSE}
        procedure PostMessage(Message: Word; WParam: Word; LParam: NativeInt);
        {$ENDIF}
        procedure StopThread;
    end;

function NewString(const Text: string): NativeInt;
begin
    Result := 0;
    string(Result) := Text;
end;

function FreeString(var P: NativeInt): String;
begin
    NativeInt(Result) := P;
end;

{function FreeString(var P: LPARAM): String;
begin
    if LongBool(P) then begin
        SetLength(Result, Length(PChar(P)));
        Move(Pointer(P)^, Pointer(Result)^, Length(Result)*SizeOfChar);
        P := LocalFree(HLOCAL(P));
    end;
end;

function NewString(const Text: string): LPARAM;
var l: Integer;
begin
    l := Length(Text)*SizeOfChar;
    if L > 0 then begin
        Result := LocalAlloc(LPTR, l+SizeOfChar);
        if LongBool(Result) then
            Move(Pointer(Text)^, Pointer(Result)^, l);
    end else
        Result := 0;
end;    }

{ TGUIThread }

procedure TGUIThread.Execute;
var Message: PThreadMessage;
    wr: TWaitResult;
begin
    while not Terminated do begin
        wr := FMessageEvent.WaitFor(INFINITE);
        if (not Terminated) and (wr = wrSignaled) then begin
            if FTimeOut then begin
                FTimeOut := False;
                if Assigned(FOwner.FOnTimeOut) then
                    FOwner.FOnTimeOut(FOwner);
            end;
            while FQueue.Count > 0 do begin
                FSection.Enter;
                Message := FQueue[0];
                FQueue.Delete(0);
                FSection.Leave;
                FCurrentMessage := Message^;
                //Move(Message^, FCurrentMessage, SizeOf(FCurrentMessage));
                FreeMem(Message);
                {$IFDEF FPC}
                Synchronize(@CallGUIThread);
                {$ELSE}
                Synchronize(CallGUIThread);
                {$ENDIF}
            end;
        end;
    end;
end;

procedure TGUIThread.CallGUIThread;
begin
    if Assigned(FOwner) then
        FOwner.OnThreadReceiveMessage(FOwner, FCurrentMessage);
end;

constructor TGUIThread.Create(AOwner: TWThread);
begin
    inherited Create(False);
    FMessageEvent := TEvent.Create(nil, False, False, '');
    FQueue := TList.Create;
    FSection := TCriticalSection.Create;
    FOwner := AOwner;
end;

destructor TGUIThread.Destroy;
begin
    FSection.Free;
    FQueue.Free;
    FMessageEvent.Free;
    inherited Destroy;
end;

{$IFDEF FPC}
procedure TGUIThread.PostMessage(Message: DWord; WParam: Word; LParam: NativeInt);
{$ELSE}
procedure TGUIThread.PostMessage(Message: Word; WParam: Word; LParam: NativeInt);
{$ENDIF}
var Msg: PThreadMessage;
begin
    GetMem(Msg, SizeOf(TThreadMessage));
    Msg^.Message := Message;
    Msg^.WParam := WParam;
    Msg^.LParam := LParam;
    FSection.Enter;
    FQueue.Add(Msg);
    FSection.Leave;
    FMessageEvent.SetEvent;
end;

procedure TGUIThread.StopThread;
begin
    Terminate;
    FMessageEvent.SetEvent;
end;

{ TWThread }

procedure TWThread.SetTimeOut(AValue: Cardinal);
begin
    if FTimeOut = AValue then Exit;
    FTimeOut := AValue;
    if not Suspended then
        FMessageEvent.SetEvent;
end;

procedure TWThread.DoTimeOut;
begin
    TGUIThread(FGUIThread).FTimeOut := true;
    TGUIThread(FGUIThread).FMessageEvent.SetEvent;
end;

procedure TWThread.Execute;
var Message: PThreadMessage;
    wr: TWaitResult;
begin
    while not Terminated do begin
        wr := FMessageEvent.WaitFor(FTimeOut);
        if not Terminated then
            case WR of
                wrSignaled: while FQueue.Count > 0 do begin
                    FSection.Enter;
                    Message := FQueue[0];
                    FQueue.Delete(0);
                    FSection.Leave;
                    Dispatch(Message^);
                    FreeMem(Message);
                end;
                wrTimeout: begin
                    if FTimeOutIsDirect then
                        DirectTimeOut
                    else
                        DoTimeOut;
                end;
            end;
    end;
end;

{$IFDEF FPC}
procedure TWThread.SendGUIMessage(Message: DWord; WParam: Word; LParam: NativeInt);
{$ELSE}
procedure TWThread.SendGUIMessage(Message: Word; WParam: Word; LParam: NativeInt);
{$ENDIF}
begin
    if Assigned(FGUIThread) then
        TGUIThread(FGUIThread).PostMessage(Message, WParam, LParam);
end;

constructor TWThread.Create;
begin
    Create(False);
end;

constructor TWThread.Create(CreateSuspended: Boolean);
begin
    inherited Create(CreateSuspended);
    FQueue := TList.Create;
    FMessageEvent := TEvent.Create(nil, False, False, '');
    FSection := TCriticalSection.Create;
    FTimeOut := INFINITE;
    FTimeOutIsDirect := False;
    FGUIThread := TGUIThread.Create(Self);
end;

destructor TWThread.Destroy;
begin
    FGUIThread.Free;
    FMessageEvent.Free;
    FQueue.Free;
    FSection.Free;
    inherited Destroy;
end;

procedure TWThread.DirectTimeOut;
begin
    // override
end;

{$IFDEF FPC}
procedure TWThread.PostMessage(Message: DWord; WParam: Word; LParam: NativeInt);
{$ELSE}
procedure TWThread.PostMessage(Message: Word; WParam: Word; LParam: NativeInt);
{$ENDIF}
var Msg: PThreadMessage;
begin
    GetMem(Msg, SizeOf(TThreadMessage));
    Msg^.Message := Message;
    Msg^.WParam := WParam;
    Msg^.LParam := LParam;
    FSection.Enter;
    FQueue.Add(Msg);
    FSection.Leave;
    FMessageEvent.SetEvent;
end;

procedure TWThread.StopThread;
begin
    TGUIThread(FGUIThread).StopThread;
    Terminate;
    FMessageEvent.SetEvent;
end;

end.



Проект под Lazarus:
Код: 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.
unit Unit1;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, wthread,
  Messages, ExtCtrls, StdCtrls;

const
  WM_STRING = WM_USER+$120;

type

  { TMyThread }

  TMyThread = class(TWThread)
  private
      procedure ReceiveString(var Msg: TThreadMessage); message WM_STRING;
  end;

  { TForm1 }

  TForm1 = class(TForm)
      Button1: TButton;
      Button2: TButton;
      Memo1: TMemo;
      procedure Button1Click(Sender: TObject);
      procedure Button2Click(Sender: TObject);
      procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);
  private
      FThread: TMyThread;
      procedure StartThread;
      procedure StopThread;
  public
      procedure ReceiveMsg(Sender: TWThread; Msg: TThreadMessage);
  end;

var
  Form1: TForm1;

implementation

{$R *.lfm}

{ TForm1 }

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

procedure TForm1.Button2Click(Sender: TObject);
var s: string;
begin
    s := '';
    if LongBool(FThread) and InputQuery('Input', 'Enter text', s) then begin
        // отправляем введенную строку в доп. поток
        Memo1.Lines.Add(Format('-> %s', [s]));
        FThread.PostMessage(WM_STRING, 0, NewString(s));
        s := '';
    end;
end;

procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction);
begin
    if LongBool(FThread) then
        StopThread;
end;

procedure TForm1.ReceiveMsg(Sender: TWThread; Msg: TThreadMessage);
begin
    if Msg.Message = WM_STRING then
        Memo1.Lines.Add(Format('<- %s', [FreeString(Msg.LParam)]));
end;

procedure TForm1.StartThread;
begin
    if not LongBool(FThread) then begin
        FThread := TMyThread.Create(false);
        {$IFDEF FPC}
        FThread.OnThreadReceiveMessage := @ReceiveMsg;
        {$ELSE}
        FThread.OnThreadReceiveMessage := ReceiveMsg;
        {$ENDIF}
    end;
end;

procedure TForm1.StopThread;
begin
    if LongBool(FThread) then begin
        FThread.StopThread;
        FThread.Free;
        FThread := nil;
    end;
end;

{ TMyThread }

procedure TMyThread.ReceiveString(var Msg: TThreadMessage);
var s: string;
begin
   s := FreeString(Msg.LParam);
   SendGUIMessage(WM_STRING, 0, NewString(s));
   Sleep(5000);
   SendGUIMessage(WM_STRING, 0, NewString(s));
end;

end.



Проект под Delphi:
Код: 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.
unit LazMain;

interface

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

const
  WM_STRING = WM_USER+$120;

type
    { TMyThread }

    TMyThread = class(TWThread)
    private
        procedure ReceiveString(var Msg: TThreadMessage); message WM_STRING;
    end;

  TfrmThreadTest = class(TForm)
    Memo1: TMemo;
    Button1: TButton;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    FThread: TMyThread;
    procedure StartThread;
    procedure StopThread;
  public
    procedure ReceiveMsg(Sender: TWThread; Msg: TThreadMessage);
  end;

var
  frmThreadTest: TfrmThreadTest;

implementation

{$R *.dfm}

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

procedure TfrmThreadTest.Button2Click(Sender: TObject);
var s: string;
begin
    s := '';
    if LongBool(FThread) and InputQuery('Input', 'Enter text', s) then begin
        // отправляем введенную строку в доп. поток
        Memo1.Lines.Add(Format('-> %s', [s]));
        FThread.PostMessage(WM_STRING, 0, NewString(s));
        s := '';
    end;
end;

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

procedure TfrmThreadTest.ReceiveMsg(Sender: TWThread; Msg: TThreadMessage);
begin
    if Msg.Message = WM_STRING then
        Memo1.Lines.Add(Format('<- %s', [FreeString(Msg.LParam)]));
end;

procedure TfrmThreadTest.StartThread;
begin
    if not LongBool(FThread) then begin
        FThread := TMyThread.Create(false);
        {$IFDEF FPC}
        FThread.OnThreadReceiveMessage := @ReceiveMsg;
        {$ELSE}
        FThread.OnThreadReceiveMessage := ReceiveMsg;
        {$ENDIF}
    end;
end;

procedure TfrmThreadTest.StopThread;
begin
    if LongBool(FThread) then begin
        FThread.StopThread;
        FThread.Free;
        FThread := nil;
    end;
end;

{ TMyThread }

procedure TMyThread.ReceiveString(var Msg: TThreadMessage);
var s: string;
begin
   s := FreeString(Msg.LParam);
   SendGUIMessage(WM_STRING, 0, NewString(s));
   Sleep(5000);
   SendGUIMessage(WM_STRING, 0, NewString(s));
end;

end.



Результаты в обоих средах разработки одинаковы. :)
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38445526
Фотография wadman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Кстати, кто-нибудь возьмется протестировать под линуксом? Очень хочется узнать результат кросс-платформенности.
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38446027
white_nigger
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Зря на Dimonka наехали, он более грамотную архитектуру предлагал
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38446154
Фотография wadman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
white_niggerЗря на Dimonka наехали, он более грамотную архитектуру предлагал
Ровно с тем-же количеством аргументов можно и опровергнуть эту грамотность.
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38446273
white_nigger
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Я вижу три аргумента.
1. Разделение логики. Нет мешанины в коде. Т.е. шаблон превращается по факту в готовый, расширяемый паттерн.
2. За выноса логики юзер-работы появляется легкая возможность покрытия её тестом.
3. Гм... а вы в команде работали? Сопровождать дешевле граммотную архитектуру

Ну в-общем каждый судит со своего опыта. Можно отнестись "работает - да и ладно", а можно продумать на будущее
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38446289
Фотография wadman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
white_niggerа можно продумать на будущее
Для ленивых в инете полно оберток в виде компонент и даже целых библиотек для работы с потоками. Я-же предложил свою модель, без наворотов, но покрывающую большинство мелких задач с минимумом типичных ошибок. У каждого своя ниша.

Димонка - тролль, который сочиняет проблемы в голове, а не в коде.
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38446466
white_nigger
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Насчет спойлера - не знаю, не следил за его постами. Просто в данном, конкретном случае его подход мне больше импонирует. Причины я отписал выше. В любом случае - мир, дружба, жвачка
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38446916
Фотография Dimonka
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
wadmanДимонка - тролль, который сочиняет проблемы в голове, а не в коде.
Спасибо, конечно, за оценку. Но в век ООП и предметности пересылать из потока в поток практически машинные примитивы - это огромное достижение. То что нужно 99.9% программистов ежедневно.

Я вижу сутуацию примерно так: ты придумал проблему, успешно её решил, а все кто считает, что проблемы-то в основном у людей другие, - тролли и негодяи. Жаль что меня забанят, но не мог не ответить.
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38446935
Гаджимурадов Рустам
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Dimonka> Жаль что меня забанят, но не мог не ответить.

Не, пока не забанят. Но предлагаю озвучить, какая
проблема(ы), по-твоему, более насущна и её (их) решение.

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

Не, пока не забанят. Но предлагаю озвучить, какая
проблема(ы), по-твоему, более насущна и её (их) решение.

Одно из решений я уже предлагал выше. Ну а проблемы старые:
1. вынести пользовательский код в поток
2. передать этому коду данные
3. узнать, что код выполнен (данные обработаны)

Если первый и третий пункт в "шаблоне" более менее решён, то второй решается, только через извращения (примитивы), которые автор топика называет API. Опять же, данные обработаны, но с передачей результатов работы те же проблемы, что и в пункте 2.

Очень не понимаю, почему автор топика так агресивен к достаточно контруктивной (на мой взгляд) критике.
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38446975
Фотография wadman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
DimonkaОчень не понимаю, почему автор топика так агресивен к достаточно контруктивной (на мой взгляд) критике.
14918946

Еще раз: у этого решения своя ниша, минимум наворотов и, соответственно, ошибок. Особенно для новичков, которых-то голый TThread запутывает.
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38447032
Гаджимурадов Рустам
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Dimonka> Одно из решений я уже предлагал выше

C кодом?

Posted via ActualForum NNTP Server 1.5
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38447040
Фотография wadman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Марат Сафин, кстати, твой вариант с TValue/Variant не выдерживает мой тест в дельфи. В лазарусе - все отлично. Потому пришлось отказаться в пользу LParam, чтоб работало одинаково на обоих платформах.
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38447176
Фотография Dimonka
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Гаджимурадов РустамDimonka> Одно из решений я уже предлагал выше
C кодом?
По крайней мере с кодом возможного интерфейса. Я не обещал дать готовое решение. Просто предлагал способ обойти недостатки данного решения.
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38447387
Марат Сафин
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
wadman,
Какой ешё тест?
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38447436
Фотография wadman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Марат Сафинwadman,
Какой ешё тест?
Который придумал Димонка 15047828 Если жать "тест" (Button2) подряд 5 раз и вводить с 1о-го по 5-ть, то вылетает ошибка. Текст не помню, но воспроизвести не составит труда.
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38447692
Марат Сафин
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
wadmanМарат Сафин, кстати, твой вариант с TValue/Variant не выдерживает мой тест в дельфи. В лазарусе - все отлично. Потому пришлось отказаться в пользу LParam, чтоб работало одинаково на обоих платформах.
Там метод Destroy переделать нужно, сначала вызывать inherited а потом уничтожать объекты, я забыл про эту особенность TThread. то есть вот так:
Код: pascal
1.
2.
3.
4.
5.
6.
destructor TWThread.Destroy;
begin
  inherited;
  FMsgEvent.Free;
  FQueue.Free;
end;
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38447693
Марат Сафин
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Других косяков не нашёл, у меня работает на ура.
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38447745
Марат Сафин
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Dimonka,
Писал я год назад такую вот штуку возможно это то, что вы хотели.
...
Рейтинг: 0 / 0
25 сообщений из 469, страница 4 из 19
Форумы / Delphi [игнор отключен] [закрыт для гостей] / Шаблон класса для работы с потоком (WThread, Thread)
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


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