powered by simpleCommunicator - 2.0.61     © 2026 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / Delphi [игнор отключен] [закрыт для гостей] / Шаблон класса для работы с потоком (WThread, Thread)
25 сообщений из 469, страница 10 из 19
Шаблон класса для работы с потоком (WThread, Thread)
    #38948492
Фотография wadman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Перенес messages, TList заменил на TQueue и по мелочи.
Код: 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.
332.
333.
334.
335.
336.
337.
338.
339.
340.
341.
342.
343.
344.
345.
346.
347.
348.
349.
350.
351.
352.
353.
354.
355.
356.
357.
358.
359.
360.
361.
362.
363.
364.
365.
366.
367.
368.
369.
370.
371.
372.
373.
374.
375.
376.
377.
378.
379.
380.
381.
382.
383.
384.
385.
386.
387.
388.
389.
390.
391.
392.
393.
394.
395.
396.
397.
398.
399.
400.
401.
402.
403.
404.
405.
406.
407.
408.
409.
410.
411.
412.
413.
414.
415.
416.
417.
418.
419.
420.
421.
422.
423.
424.
425.
426.
427.
428.
429.
430.
431.
432.
433.
434.
435.
436.
437.
438.
439.
440.
441.
442.
443.
444.
445.
446.
447.
448.
449.
450.
451.
452.
453.
454.
455.
456.
457.
458.
459.
460.
461.
462.
463.
464.
465.
466.
467.
468.
469.
470.
471.
472.
473.
474.
475.
476.
477.
478.
479.
480.
481.
482.
483.
484.
485.
486.
487.
488.
489.
490.
491.
492.
493.
494.
495.
496.
497.
498.
499.
500.
501.
502.
503.
504.
505.
506.
507.
508.
509.
510.
511.
512.
513.
514.
515.
516.
517.
518.
519.
520.
521.
522.
523.
524.
525.
526.
527.
528.
529.
530.
531.
532.
533.
534.
535.
536.
537.
538.
539.
540.
541.
542.
543.
544.
545.
546.
547.
548.
549.
550.
551.
552.
553.
554.
555.
556.
557.
558.
559.
560.
561.
562.
563.
564.
565.
566.
567.
568.
569.
570.
571.
572.
573.
574.
575.
576.
577.
578.
579.
580.
581.
582.
583.
584.
585.
586.
587.
588.
589.
590.
591.
592.
593.
594.
595.
596.
597.
598.
599.
600.
601.
602.
603.
604.
605.
606.
607.
608.
609.
610.
611.
612.
613.
614.
615.
616.
617.
618.
619.
620.
621.
622.
623.
624.
625.
626.
627.
628.
629.
630.
631.
632.
633.
634.
635.
636.
637.
638.
639.
640.
641.
642.
643.
644.
645.
646.
647.
648.
649.
650.
651.
652.
653.
654.
655.
656.
657.
658.
659.
660.
661.
662.
663.
664.
665.
666.
667.
668.
669.
670.
671.
672.
673.
674.
675.
676.
677.
678.
679.
680.
681.
682.
683.
684.
685.
686.
687.
688.
689.
690.
691.
692.
693.
694.
695.
696.
unit WThread;
// модуль для работы с доп.потоками Delphi&Lazarus (win&linux)
// позволяет "общаться" дополнительному и основному потокам посредством очереди сообщений
// (c) wadman 2013-2015, версия от 29.04.2015
//
// использование:
// 1. Создать наследника с объявленными обработчиками сообщений
//  const WM_TEST_PROC = WM_THREAD_BASE + 1;
//  TMyThread = class(TWThread)
//     procedure WMTestProc(var Msg: TThreadMessage); message WM_TEST_PROC;
//  данные процедуры будут выполняться в доп.потоке путем отправки связанного с ними сообщения в поток
//  см. PostToThreadMessage
// 2. Присвоить форме обработчика(ов) события OnThreadReceiveMessage (OnTimeOut - при необходимости)
//
// Для обмена строками рекомендуется использовать функции NewString и FreeString
//
// Для корректной остановки потока и дальнейшей работы программы должна использоваться процедура StopThrad
// При компиляции под *nix в файле проекта до раздела USES вставить строку строку {$DEFINE UseCThreads}
{$IFDEF FPC}
{$mode objfpc}{$H+}
    {$IFDEF WINDOWS}
        {$DEFINE FPC_WIN}
        {$DEFINE ALL_WIN}
    {$ELSE}
        {$DEFINE FPC_NIX}
    {$ENDIF}
{$ELSE}
    {$DEFINE WIN}
    {$DEFINE ALL_WIN}
{$ENDIF}

// для передачи строк между потоками используется выделенная память
{$DEFINE ALLOC_STRING}

interface

uses
    SysUtils,
{$IFDEF UNIX} // добавить эти строки в модуль проекта при разработке не под windows
        //cthreads,
        //cmem,
{$ENDIF}
    Classes
    ,contnrs
{$IFNDEF FPC}
    ,Messages
{$ENDIF}
{$IFDEF FPC_WIN}
    ,Windows
{$ENDIF}
{$IFDEF FPC}
    ,SyncObjs
{$ENDIF}
    ;

const
{$IFDEF FPC}
    //INFINITE            = Cardinal($FFFFFFFF);
{$ENDIF}
    WM_USER             = $400;
    WM_THREAD_BASE      = WM_USER + $110;
    WM_THREAD_MAX       = WM_USER + $300;

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

    TWThread = class;

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

    { TWThread }

    TWThread = class(TThread)
    private
{$IFDEF FPC}
        FQueue: TQueue;
        FSection: TCriticalSection;
        FMessageEvent: TEvent;
        FGUIThread: TThread;
{$ELSE}
        FQueueReady: boolean;
        FQueueMessages: array of TThreadMessage;
        FToolWindow: THandle;
        hCloseEvent: THandle;
{$ENDIF}
        FOnThreadReceiveMessage: TWThreadReceiveMessage;
        FOnTimeOut: TWThreadTimeOut;
        FTimeOutIsDirect: boolean;
        function GetMsFromDateTime(const Value: TDateTime): Cardinal;
        procedure SetTimeOut(const Value: Cardinal);
    protected
        FTimeOut: Cardinal;
{$IFNDEF FPC}
        FWindowHandle: THandle;
        procedure WWindowProc(var Msg: TMessage);
        procedure GetWindow;
        procedure FreeWindow;
{$ENDIF}
            // отправка сообщения из этого потока для вызова обработчика OnThreadReceiveMessage
        procedure PostMessageFromThread(const Msg: Word; const WParam: Word; const LParam: NativeInt);
        procedure DoThreadReceiveMessage(const Msg: Word; const WParam: Word; const LParam: NativeInt);
        procedure DoTimeout;
        procedure Execute; override;
        function GetTimeOut: Cardinal; virtual;
    public
        constructor Create; overload;
        constructor Create(CreateSuspended: Boolean); overload;
        destructor Destroy; override;
            // Две процедуры, которые выполняются в контексте потока. В начале и в конце.
        procedure InitThread; virtual;
        procedure DoneThread; virtual;
            // см TimeOutIsDirect, при true - перекрыть следующую процедуру
        procedure DirectTimeOut; virtual;
            // отправка любого сообщения В этот поток
        function PostToThreadMessage(const Msg: Word; const WParam: Word; const LParam: NativeInt): Boolean;
            // остановка потока по феншую
        procedure StopThread;
            // событие на получение данных ИЗ этого потока, вызывается в основном потоке
        property OnThreadReceiveMessage: TWThreadReceiveMessage read FOnThreadReceiveMessage write FOnThreadReceiveMessage;
            // событие, возникающее при превышении интервала ожидания
        property OnTimeOut: TWThreadTimeOut read FOnTimeOut write FOnTimeOut;
            // интервал ожидания в мс, по-умолчанию - бесконечно
        property TimeOut: Cardinal read GetTimeOut 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

{$IFNDEF FPC}
uses Windows;
{$ENDIF}

const
{$IFNDEF WINDOWS}
    WM_THREAD_READY     = WM_USER+$102;
{$ENDIF}
    WM_TIMEOUT          = WM_USER+$101;

    SizeOfChar          = SizeOf(Char);

    // константы из DateUtils
    OneMillisecond      = 1 / MSecsPerDay;

{$IFDEF ALLOC_STRING}
{$IFDEF ALL_WIN}
// для передачи строки между программами
function GlobalNewString(const Text: string): NativeInt;
var l: Integer;
    p: pointer;
begin
    l := Length(Text)*SizeOfChar;
    if L > SizeOfChar then begin
        Result := GlobalAlloc(GHND, l+SizeOfChar);
        if LongBool(Result) then begin
            p := GlobalLock(Result);
            Move(Pointer(Text)^, Pointer(p)^, l);
            GlobalUnlock(Result);
        end
    end else
        Result := 0;
end;

// для передачи строки между программами
function GlobalFreeString(var P: NativeInt): String;
var ps: pointer;
begin
    if LongBool(P) then begin
        ps := GlobalLock(P);
        SetLength(Result, Length(PChar(ps)));
        Move(Pointer(ps)^, Pointer(Result)^, Length(Result)*SizeOfChar);
        GlobalUnlock(P);
        P := GlobalFree(P);
    end;
end;
{$ENDIF}

// для передачи строки в пределах одной программы
function FreeString(var P: NativeInt): String;
begin
{$HINTS OFF}
    if LongBool(P) then begin
        SetLength(Result, Length(PChar(P)));
        Move(Pointer(P)^, Pointer(Result)^, Length(Result)*SizeOfChar);
{$IFDEF ALL_WIN}
        P := LocalFree(HLOCAL(P));
{$ELSE}
        P := FreeMem(Pointer(P));
{$ENDIF}
    end;
end;

// для передачи строки в пределах одной программы
function NewString(const Text: string): NativeInt;
var l: Integer;
begin
    l := Length(Text)*SizeOfChar;
    if L > SizeOfChar then begin
{$IFDEF ALL_WIN}
        Result := LocalAlloc(LPTR, l+SizeOfChar);
{$ELSE}
        Pointer(Result) := AllocMem(l+SizeOfChar);
{$ENDIF}
        if LongBool(Result) then
            Move(Pointer(Text)^, Pointer(Result)^, l);
{$HINTS ON}
    end else
        Result := 0;
end;
{$ELSE}
function NewString(const Text: string): NativeInt;
begin
    Result := 0;
    string(Result) := Text;
end;

function FreeString(var P: NativeInt): String;
begin
    Result := '';
    NativeInt(Result) := P;
end;
{$ENDIF}

{$IFDEF FPC}
type

    { TGUIThread }

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

{ TGUIThread }

procedure FreeQueue(const Queue: TQueue);
begin
    while Queue.Count > 0 do
        Queue.Pop;
end;

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.Pop;
                FSection.Leave;
                FCurrentMessage := Message^;
                FreeMem(Message);
                if (FCurrentMessage.Message >= WM_THREAD_BASE)
                    and(FCurrentMessage.Message <= WM_THREAD_MAX) then
                    Synchronize(@CallGUIThread);
            end;
        end;
    end;
end;

procedure TGUIThread.CallGUIThread;
begin
    if Assigned(FOwner) then
        FOwner.DoThreadReceiveMessage(FCurrentMessage.Message, FCurrentMessage.WParam, FCurrentMessage.LParam);
end;

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

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

procedure TGUIThread.PostMessage(const Message: DWord; const WParam: Word; const LParam: NativeInt);
var Msg: PThreadMessage;
begin
    GetMem(Msg, SizeOf(TThreadMessage));
    Msg^.Message := Message;
    Msg^.WParam := WParam;
    Msg^.LParam := LParam;
    FSection.Enter;
    FQueue.Push(Msg);
    FSection.Leave;
    FMessageEvent.SetEvent;
end;

procedure TGUIThread.StopThread;
begin
    Terminate;
    FMessageEvent.SetEvent;
end;
{$ENDIF}

{ TWThread }

constructor TWThread.Create(CreateSuspended: Boolean);
begin
    inherited Create(CreateSuspended);
{$IFDEF FPC}
    FQueue := TQueue.Create;
    FMessageEvent := TEvent.Create(nil, False, False, '');
    FSection := TCriticalSection.Create;
    FGUIThread := TGUIThread.Create(Self);
{$ELSE}
    hCloseEvent := CreateEvent(nil, false, false, nil);
    GetWindow;
    FWindowHandle := 0;
    FQueueReady := False;
{$ENDIF}
    FTimeOut := INFINITE;
    FTimeOutIsDirect := False;
end;

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

destructor TWThread.Destroy;
begin
{$IFDEF FPC}
    FMessageEvent.Free;
    FreeQueue(FQueue);
    FQueue.Free;
    FGUIThread.Free;
    FSection.Free;
{$ELSE}
    CloseHandle(hCloseEvent);
{$ENDIF}
    inherited;
end;

procedure TWThread.DirectTimeOut;
begin
    // override
end;

// Процедура, выполняющая в контексте этого потока перед запуском очереди сообщений (в начале работы потока).
procedure TWThread.InitThread;
begin
    // empty
end;

// Процедура, выполняющая в контексте этого потока после отработки очереди сообщений (в конце работы потока).
procedure TWThread.DoneThread;
begin
    // empty
end;

procedure TWThread.DoThreadReceiveMessage(const Msg: Word; const WParam: Word; const LParam: NativeInt);
var ThreadMsg: TThreadMessage;
begin
    if Assigned(FOnThreadReceiveMessage) then begin
        ThreadMsg.Message := Msg;
        ThreadMsg.WParam := WParam;
        ThreadMsg.LParam := LParam;
        FOnThreadReceiveMessage(Self, ThreadMsg);
    end;
end;

procedure TWThread.DoTimeout;
begin
{$IFDEF FPC}
    TGUIThread(FGUIThread).FTimeOut := true;
    TGUIThread(FGUIThread).FMessageEvent.SetEvent;
{$ELSE}
    if Assigned(FOnTimeOut) then
        FOnTimeOut(Self);
{$ENDIF}
end;

// отправка любого сообщения В этот поток
function TWThread.PostToThreadMessage(const Msg: Word; const WParam: Word;
    const LParam: NativeInt): Boolean;
{$IFDEF FPC}
var PMsg: PThreadMessage;
begin
    GetMem(PMsg, SizeOf(TThreadMessage));
    PMsg^.Message := Msg;
    PMsg^.WParam := WParam;
    PMsg^.LParam := LParam;
    FSection.Enter;
    FQueue.Push(PMsg);
    FSection.Leave;
    FMessageEvent.SetEvent;
    result := true;
{$ELSE}
begin
    if FQueueReady then begin
        // если очередь сообщений создана и поток инициализирован, то сразу отправляем
        result := (not Suspended)and(PostThreadMessage(ThreadID, Msg, wParam, lParam));
    end else begin
        // иначе кэшируем
        SetLength(FQueueMessages, Length(FQueueMessages)+1);
        FQueueMessages[High(FQueueMessages)].Message := Msg;
        FQueueMessages[High(FQueueMessages)].WParam := WParam;
        FQueueMessages[High(FQueueMessages)].LParam := LParam;
        result := True;
    end;
{$ENDIF}
end;

function TWThread.GetMsFromDateTime(const Value: TDateTime): Cardinal;
begin
    Result := Round((Now - Value) / OneMillisecond);
end;

procedure TWThread.SetTimeOut(const Value: Cardinal);
begin
{$IFDEF FPC}
    if FTimeOut <> Value then begin
        if (Value = 0) then
            FTimeOut := INFINITE
        else
            FTimeOut := Value;
        if not Suspended then
            FMessageEvent.SetEvent;
    end;
{$ELSE}
    if FTimeOut <> Value then begin
        if (Value = 0) then
            FTimeOut := INFINITE
        else
            FTimeOut := Value;
        if (not Suspended)and(GetCurrentThreadId <> ThreadID) then
            PostThreadMessage(ThreadID, WM_TIMEOUT, Value, 0);
    end;
{$ENDIF}
end;

procedure TWThread.Execute;
var
    internalTimeout, ms: Cardinal;
    busy: TDateTime;
{$IFDEF FPC}
    Message: PThreadMessage;
    wr: TWaitResult;
begin
    InitThread;
    internalTimeout := TimeOut;
    busy := 0;
    while not Terminated do begin
        wr := FMessageEvent.WaitFor(internalTimeout);
        busy := Now;
        if not Terminated then
            case WR of
                wrSignaled: while FQueue.Count > 0 do begin
                    FSection.Enter;
                    Message := FQueue.Pop;
                    FSection.Leave;
                    if (Message^.message >= WM_THREAD_BASE)
                        and (Message^.Message <= WM_THREAD_MAX) then
                            Dispatch(Message^);
                    FreeMem(Message);
                end;
                wrTimeout: begin
                    internalTimeout := TimeOut;
                    if FTimeOutIsDirect then begin
                        DirectTimeOut;
                    end else begin
                        PostMessageFromThread(WM_TIMEOUT, 0, 0);
                        // сообщение не занимает много времени, на точность не влияет
                        busy := 0;
                    end;
                end;
            end;
        // корректируем интервал таймаута на время выполнения кода выше
        if (busy <> 0) then begin
            ms := GetMsFromDateTime(busy);
            if (ms > TimeOut) then
                internalTimeout := 0
            else
                internalTimeout := TimeOut - ms;
        end else begin
            internalTimeout := TimeOut;
        end;
    end;
{$ELSE}
    message: TThreadMessage;
    HandlesToWaitFor: array [0 .. 0] of THandle;
    dwHandleSignaled: DWORD;
    msg: TMsg;

begin
    // следующая строка должна быть всегда первой в процедуре, т.к. запускает очередь сообщений для потока
    PeekMessage(msg, THandle(-1), WM_USER, WM_USER, PM_NOREMOVE);
    internalTimeout := TimeOut;
    busy := 0;
    InitThread;
    HandlesToWaitFor[0] := hCloseEvent;
    PostMessageFromThread(WM_THREAD_READY, 0, 0);

    while not Terminated do begin
        if ((FWindowHandle = 0) and(not PeekMessage(msg, THandle(-1), WM_USER, WM_THREAD_MAX, PM_REMOVE)))
            or((FWindowHandle <> 0) and (
                (not PeekMessage(msg, FWindowHandle, 0, 0, PM_REMOVE))
                and
                (not PeekMessage(msg, THandle(-1), WM_USER, WM_THREAD_MAX, PM_REMOVE))
            )) then begin

            if (FWindowHandle = 0) then
                dwHandleSignaled := MsgWaitForMultipleObjects(1, HandlesToWaitFor, false, internalTimeout, QS_ALLINPUT)
            else
                dwHandleSignaled := MsgWaitForMultipleObjects(1, HandlesToWaitFor, false, internalTimeout, QS_ALLEVENTS);
            busy := Now;
            case dwHandleSignaled of
                WAIT_OBJECT_0: begin // выставлено событие остановки потока
                    if not FreeOnTerminate then
                        Terminate;
                    break;
                end;
                WAIT_OBJECT_0 + 1: begin // получено сообщение
                    Continue;
                end;
                WAIT_TIMEOUT: begin
                    if FTimeOutIsDirect then begin
                        DirectTimeOut;
                    end else begin
                        PostMessageFromThread(WM_TIMEOUT, 0, 0);
                        // сообщение не занимает много времени, на точность не влияет
                        busy := 0;
                    end;
                    // выставляем новый интервал за минусом времени выполнения процедуры таймаута
                    if (busy <> 0) then begin
                        ms := GetMsFromDateTime(busy);
                        if (ms > TimeOut) then
                            internalTimeout := 0
                        else
                            internalTimeout := TimeOut - ms;
                    end else begin
                        internalTimeout := TimeOut;
                    end;
                    Continue;
                end;
            end;
        end;

        if Terminated then break;

        if LongBool(FWindowHandle) and (FWindowHandle = msg.hwnd) then begin
            // хэндл окна присвоен и пришло сообщение для окна
            TranslateMessage(msg);
            DispatchMessage(msg);
        end else case msg.message of
            WM_TIMEOUT: begin
                // изменен таймаут
                FTimeOut := msg.wParam;
                busy := 0;
            end;
            WM_THREAD_BASE..WM_THREAD_MAX: begin
                message.Message := Msg.message;
                message.WParam := Msg.wParam;
                message.LParam := Msg.lParam;
                // ищем и вызываем procedure of object message
                Dispatch(message);
            end;
        end;

        // корректируем интервал таймаута на время выполнения кода выше
        if (busy <> 0) then begin
            ms := GetMsFromDateTime(busy);
            if (ms > TimeOut) then
                internalTimeout := 0
            else
                internalTimeout := TimeOut - ms;
        end else begin
            internalTimeout := TimeOut;
        end;
    end;
{$ENDIF}
    DoneThread;
end;

// отправка сообщения из этого потока для вызова обработчика OnThreadReceiveMessage
procedure TWThread.PostMessageFromThread(const Msg: Word; const WParam: Word; const LParam: NativeInt);
begin
{$IFDEF FPC}
    if Assigned(FGUIThread) then
        TGUIThread(FGUIThread).PostMessage(Msg, WParam, LParam);
{$ELSE}
    if FToolWindow <> 0 then begin
        PostMessage(FToolWindow, Msg, WParam, LParam);
    end;
{$ENDIF}
end;

// отправка любого сообщения В этот поток
procedure TWThread.StopThread;
begin
{$IFDEF FPC}
    TGUIThread(FGUIThread).StopThread;
    Terminate;
    FMessageEvent.SetEvent;
{$ELSE}
    FreeWindow;
    Terminate;
    SetEvent(hCloseEvent);
    //SleepEx(1, false);
    //result := WaitForSingleObject(Handle, 10000) <> WAIT_TIMEOUT;
{$ENDIF}
end;

function TWThread.GetTimeOut: Cardinal;
begin
    Result := FTimeOut;
end;

{$IFNDEF FPC}

procedure TWThread.WWindowProc(var Msg: TMessage);
var i: integer;
begin
    case Msg.Msg of
        WM_TIMEOUT: begin
            DoTimeout;
            Msg.Result := 1;
        end;
        WM_THREAD_READY: begin
            FQueueReady := true;
            // поток готов, отправляем ему все закэшированные сообщения
            if Length(FQueueMessages) > 0 then for i := Low(FQueueMessages) to High(FQueueMessages) do
                PostToThreadMessage(FQueueMessages[i].Message, FQueueMessages[i].WParam, FQueueMessages[i].LParam);
            SetLength(FQueueMessages, 0);
        end;
        WM_THREAD_BASE..WM_THREAD_MAX: begin
            DoThreadReceiveMessage(Msg.Msg, Msg.WParam, Msg.LParam);
            Msg.Result := 1;
        end
    else  if LongBool(FToolWindow) then
        Msg.Result := DefWindowProc(FToolWindow, Msg.Msg, Msg.wParam, Msg.lParam);
    end;
end;

procedure TWThread.GetWindow;
begin
    FToolWindow := AllocateHWnd(WWindowProc);
end;

procedure TWThread.FreeWindow;
begin
    DeallocateHWnd(FToolWindow);
    FToolWindow := 0;
end;

{$ENDIF}

end.



Модуль WLog на основе WThread для логирования в отдельном потоке.
Код: 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.
unit WLog;
// wlog (c) wadman, 2015, 29.04.2015
// multithread safe logging

interface

// log level
type
    TLogLevel = (WLL_MINIMUM, WLL_NORMAL, WLL_MAXIMUM, WLL_EXTRA);

var WLogFileName: string;
    WLogEnabled: boolean;
    WLogLevel: TLogLevel;

function PostToLog(const Text: string): boolean; overload; // with WLL_NORMAL
function PostToLog(const Text: string; const Level: TLogLevel): boolean; overload;

implementation

uses SysUtils, WThread;

const
    WM_LOG      = WM_THREAD_BASE + 1;

type
    PLogRecord = ^TLogRecord;
    TLogRecord = record
        DT: TDateTime;
        PString: NativeInt;
    end;

    TLogThread = class(TWThread)
    private
        procedure WMLog(var Msg: TThreadMessage); message WM_LOG;
    end;

var
    LogThread: TLogThread;
    FirstLine: boolean;

function PostToLog(const Text: string): boolean;
begin
    result := PostToLog(Text, WLL_NORMAL);
end;

function PostToLog(const Text: string; const Level: TLogLevel): boolean;
var p: PLogRecord;
    d: TDateTime;
begin
    if SmallInt(Level) <= SmallInt(WLogLevel) then begin
        result := WLogEnabled and LongBool(LogThread);
        if result then begin
            d := Now;
            p := AllocMem(SizeOf(TLogRecord));
            p^.DT := d;
            p^.PString := NewString(Text);
{$HINTS OFF}
            result := LogThread.PostToThreadMessage(WM_LOG, 0, NativeInt(p));
{$HINTS ON}
            if not result then begin
                FreeString(p^.PString);
                FreeMem(p);
            end;
        end;
    end else
        Result := true;
end;

function InitLogs: boolean;
begin
    WLogFileName := ChangeFileExt(ParamStr(0), '.log');
    LogThread := TLogThread.Create;
    result := LongBool(LogThread);
end;

procedure DoneLogs;
begin
    if LongBool(LogThread) then begin
        LogThread.StopThread;
        LogThread.Free;
    end;
end;

function AddToLog(const DT: TDateTime; const Text: string): boolean;
var t: TextFile;
begin
    result := false;
    try
        AssignFile(t, WLogFileName);
        if (FileExists(WLogFileName)) then begin
            Append(t);
            if FirstLine then begin
                WriteLn(t, '');
            end;
        end else begin
            Rewrite(t);
        end;
        FirstLine := false;
        WriteLn(t, Format('%s : %s', [FormatDateTime('dd.mm.yyyy hh:nn:ss.zzz', DT), Text]));
        CloseFile(t);
        result := true;
    finally
    end;
end;

{ TLogThread }

procedure TLogThread.WMLog(var Msg: TThreadMessage);
var p: PLogRecord;
begin
{$HINTS OFF}
    p := PLogRecord(Msg.LParam);
{$HINTS ON}
    AddToLog(p^.DT, FreeString(p^.PString));
    FreeMem(p);
end;

initialization
    WLogLevel := WLL_NORMAL;
    WLogEnabled := InitLogs;
    FirstLine := true;

finalization
    DoneLogs;

end.

...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38950040
Гаджимурадов Рустам
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Почистил.

Но таки удобнее иметь файл для скачивания, при портянках таких размеров.
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38950046
Фотография wadman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Гаджимурадов Рустам, имеешь в виду дублировать?
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38950055
kealon(Ruslan)
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
wadman,
смотри, ещё такая штучка
Код: pascal
1.
2.
3.
4.
5.
constructor TWThread.Create(CreateSuspended: Boolean);
begin
    inherited Create(CreateSuspended);
   .....................
end;


если заменить на
Код: pascal
1.
2.
3.
4.
5.
6.
constructor TWThread.Create(CreateSuspended: Boolean);
begin
    inherited Create(true);
   .....................
   if not CreateSuspended then Start;
end;


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

что по твоему будет если вот это
Код: pascal
1.
2.
3.
4.
5.
6.
procedure TGUIThread.Execute;
var Message: PThreadMessage;
    wr: TWaitResult;
begin
    while not Terminated do begin
        wr := FMessageEvent.WaitFor(INFINITE);


начнёт исполняться раньше чем создастся FMessageEvent вот здесь
Код: pascal
1.
2.
3.
4.
constructor TGUIThread.Create(AOwner: TWThread);
begin
    inherited Create(False); //после этого отдельная ветка исполнения уже создана
    FMessageEvent := TEvent.Create(nil, False, False, '');
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38950640
Фотография wadman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
kealon(Ruslan)
Код: pascal
1.
//после этого отдельная ветка исполнения уже создана


То есть некто сумеет обратиться к gui-потоку до окончания конструктора TWThread.Create?

Код: pascal
1.
2.
3.
4.
  { Always start in suspended state, will be resumed in AfterConstruction if necessary
    See Mantis #16884 }
  FHandle := BeginThread(nil, StackSize, @ThreadProc, pointer(self), CREATE_SUSPENDED,
                         FThreadID);  
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38950641
dred2k
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
kealon(Ruslan), это да, но вот так проще, нет ?
Код: pascal
1.
2.
3.
FMessageEvent := TEvent.Create(nil, False, False, '');
// создаем все, что может использовать в Execute
inherited Create(False);
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38950643
dred2k
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
wadman, эва как :)
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38950657
kealon(Ruslan)
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
dred2k, wadman

мда, умываю руки
пугает что Fixed in Version 2.6.0

надо будет в дельфи 7 посмореть
PS:я всё же лучше по старинке, так оно нам староверам спокойнее :-)
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38950674
Фотография wadman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
kealon(Ruslan), без разницы, когда пофиксили: к гуи-потоку обращение происходит из twthread.execute , когда он уже создан и крутится.
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38950728
kealon(Ruslan)
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
wadman,

да в курсе уже, механизм с AfterConstruction понятен, исходники посмотреть как-то мысли не возникало :-)
PS: в Delphi7 и раньше всё пучком, косяк в fpc тока был
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38956965
Фотография brick08
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Попробовал модуль засунуть в свою программку на Lazarus. Скомпиленная под Win32 работает без запинки, а вот под wince не хочет. Досконально не проверял из-за временного отсутствия тестовой платформы. Может кто то сталкивался с особенностями PostMessage на wince?
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38956970
Фотография defecator
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Модератор форума
brick08Попробовал модуль засунуть в свою программку на Lazarus. Скомпиленная под Win32 работает без запинки, а вот под wince не хочет. Досконально не проверял из-за временного отсутствия тестовой платформы. Может кто то сталкивался с особенностями PostMessage на wince?

а как проверял при отсутствии платформы ?
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38956974
Фотография brick08
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
defecator,
проверял на панельке временно неиспользуемой, которая сейчас задействована на производстве.
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38957024
Фотография wadman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
brick08а вот под wince не хочет
Не хочу, говорит, и все тут?
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38957067
Фотография brick08
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
wadmanНе хочу, говорит, и все тут?
явных ошибок не выдает. Экземпляр класса TWThread создается нормально. Застревает где-то на TWThread.Execute, т.к. до DirectTimeOut даже не доходит.
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38957069
Фотография wadman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
brick08wadmanНе хочу, говорит, и все тут?
явных ошибок не выдает. Экземпляр класса TWThread создается нормально. Застревает где-то на TWThread.Execute, т.к. до DirectTimeOut даже не доходит.
С какими опциями компилируешь (ОС, семейство процессоров и целевой процессор)?

Под fpc PostMessage не актуален, т.к. для общения между потоками работают event-ы и поток GUIThread.

П.С. Эмулятор-бы внятный найти...
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38957078
Фотография brick08
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
wadmanС какими опциями компилируешь (ОС, семейство процессоров и целевой процессор)?

ОС - WinCE
Семейство процессоров - arm
Целевой - По умолчанию
скачал вот такой эмулятор
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38957089
Фотография wadman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
brick08wadmanС какими опциями компилируешь (ОС, семейство процессоров и целевой процессор)?

ОС - WinCE
Семейство процессоров - arm
Целевой - По умолчанию
скачал вот такой эмулятор
На нем проект, состоящий из одной пустой формы запускается?
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38957091
Фотография Подкаблучнег
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
wadmanНа нем проект, состоящий из одной пустой формы запускается?
Да, запускается. Сделал тестовый проект. По кнопке запускает поток, который каждую секунду передает время. если нужен, могу скинуть.
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38957098
Фотография brick08
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
упс, забыл перелогиниться
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38957103
Фотография wadman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
На эмуляторе работает, теперь и сам вижу.
А на устройстве нет? Тогда стоит подключить superlog 15510599 и через него выводить отладочную информацию.
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38957115
Фотография wadman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Соврал, wlog нужен. Он в первом сообщении на этой странице.
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38957282
Фотография brick08
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
wadmanНа эмуляторе работает, теперь и сам вижу.
я неправильно выразился в предыдущем сообщении. На эмуляторе у меня тоже не работает. Похоже, что намудрил где-то. Прикладываю проект
...
Рейтинг: 0 / 0
25 сообщений из 469, страница 10 из 19
Форумы / Delphi [игнор отключен] [закрыт для гостей] / Шаблон класса для работы с потоком (WThread, Thread)
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


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