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

* Исправил и скорректировал работу по таймеру. Теперь таймер отрабатывает точно (миллисекунда в миллисекунду), учитывая время работы процедур потока.

Код: 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.
697.
unit WThread;
// модуль для работы с доп.потоками Delphi&Lazarus (win&linux)
// позволяет "общаться" дополнительному и основному потокам посредством очереди сообщений
// (c) wadman 2013-2015, версия от 23.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,
    Messages
{$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: TList;
        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
{$IFDEF ALL_WIN}
    WM_THREAD_READY     = WM_USER+$102;
{$ENDIF}
    WM_TIMEOUT          = WM_USER+$101;

    SizeOfChar          = SizeOf(Char);

    // константы из DateUtils
    OneHour             = 1 / 24;
    OneMinute           = 1 / MinsPerDay;
    OneSecond           = 1 / SecsPerDay;
    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
    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);
    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: TList;
        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 List: TList);
var i: integer;
begin
    for i := 0 to List.Count-1 do
        FreeMem(List[i]);
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[0];
                FQueue.Delete(0);
                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 := TList.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.Add(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 := TList.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.Add(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[0];
                    FQueue.Delete(0);
                    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.

...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38943497
Фотография defecator
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Модератор форума
А просто приаттачить файл к сообщению не судьба ?
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38943508
Фотография wadman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
defecatorА просто приаттачить файл к сообщению не судьба ?
По себе сужу. Отсюда качаю файлы в редких и исключительных случаях.
А так хотя-бы код видно.

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

немного посмотрел, слишком жёстко, багфиксы
Код: pascal
1.
2.
3.
4.
{$IFDEF FPC_WIN}
    ,Messages
    ,Windows
{$ENDIF}      

иначе под линуксом не компилится

Код: pascal
1.
2.
3.
{$IFDEF FPC}
    INFINITE            = Cardinal($FFFFFFFF);
{$ENDIF}

убрать, в SyncObjs описан ( в fpc 2.6 точно)

Код: pascal
1.
2.
3.
4.
                FSection.Enter;
                Message := FQueue[0];
                FQueue.Delete(0);
                FSection.Leave;  

и
Код: pascal
1.
2.
3.
    FSection.Enter;
    FQueue.Add(Msg);
    FSection.Leave;

обрамить в FSection.Enter; try ... finally FSection.Leave;


FQueue - очередь лучше в отдельный класс вынести с методами PushToFront, ...(в fpc-stl есть очень неплохие реализации)
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38944144
Фотография wadman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
kealon(Ruslan)иначе под линуксом не компилится
Под каким линуксом не компилится? FPC_WIN = WINDOWS.
kealon(Ruslan)в SyncObjs описан ( в fpc 2.6 точно)
kealon(Ruslan)обрамить в FSection.Enter; try ... finally FSection.Leave;
Учел, подумаю. :)
kealon(Ruslan)FQueue - очередь лучше в отдельный класс вынести с методами PushToFront, ...(в fpc-stl есть очень неплохие реализации)
Мне в некоторых местах нужен такой подход (с отдельной очередью для гуи) и в дельфи. То есть иногда дефайню FPC. Так что его специфические вещи мне навредят.
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38945053
kealon(Ruslan)
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
wadmankealon(Ruslan)иначе под линуксом не компилится
Под каким линуксом не компилится? FPC_WIN = WINDOWS.

этого модуля под линуксом нет, в дефайн FPC_WIN он у тебя не включён (выше вписан)
Код: pascal
1.
2.
3.
4.
5.
    Classes,
    Messages
{$IFDEF FPC_WIN}
    ,Windows
{$ENDIF}


wadmankealon(Ruslan)FQueue - очередь лучше в отдельный класс вынести с методами PushToFront, ...(в fpc-stl есть очень неплохие реализации)
Мне в некоторых местах нужен такой подход (с отдельной очередью для гуи) и в дельфи. То есть иногда дефайню FPC. Так что его специфические вещи мне навредят.
это не помешает, от TList просто унаследуй для дельфей с определением методов, а для fpc бери нативный

PS: мне кажется от окна винды стоит отказаться, ты всё равно уже очередь эмулируешь, да и от таймера(его можно отдельным потоком как источник задач создать, используя sleep или лучше TEvent.wait )

PSS:слишком сложно твоим классом пользоваться, надо упрощать - кода много, а на деле дальше While GetMsg do ... не уйти
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38945228
Фотография wadman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
kealon(Ruslan),

не хочешь признаваться, как добился, что модуль не компилируется под линуксом и не надо. :) у меня он работает.

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

не хочешь признаваться, как добился, что модуль не компилируется под линуксом и не надо. :) у меня он работает.

Модуль сложный, да. Это "плата" за легкость его использования.

вот твоя редакция последняя
Код: pascal
1.
2.
3.
4.
5.
    Classes,
    Messages
{$IFDEF FPC_WIN}
    ,Windows
{$ENDIF}


я тебе сказал что она не компилится, так как модуля Messages под линуксом нет,
предложил подправить, вот так :
Код: pascal
1.
2.
3.
4.
5.
    Classes
{$IFDEF FPC_WIN}
   , Messages
    ,Windows
{$ENDIF}


это всё что имелось ввиду
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38945813
Фотография wadman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
kealon(Ruslan), еще разок: у меня компилируется (и даже работает) под линуксом, ибо в fpc/lazarus есть модуль messages.
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38946388
dred2k
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
wadman, при использовании в главной нити сообщения не поступают в файл,
пока не пройдет цикл обработки сообщений (что очевидно).
Очередь при этом растет. А в ней всего 10000 позиций (по умолчанию),
этого может оказаться маловато - будут потери записей лога. Простой выход:
меняем
Код: pascal
1.
result := PostMessage(LogWindowHandle, WM_ADD_TO_LOG, 0, NativeInt(p));

на
Код: pascal
1.
2.
3.
4.
if(GetCurrentThreadId <> MainThreadID) then
  result := PostMessage(LogWindowHandle, WM_ADD_TO_LOG, 0, NativeInt(p))
else
  Result := Boolean(SendMessage(LogWindowHandle, WM_ADD_TO_LOG, 0, NativeInt(p)));

Ну и резалт присвоить нужно для порядка, в реализации
Код: pascal
1.
function PostToLog(const Text: string): boolean;



При высокой нагрузке будут потери записей все одно.
Я себе делал класс лога через очередь, с пишущей ниткой,
постоянно ждущей и просыпающейся лишь по эвенту
(постановка записи в очередь / терминэйт нити).
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38946394
dred2k
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Во как - тема-то вон какая...
Это про "15782361" .
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38946402
Фотография wadman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
dred2kпри использовании в главной нити сообщения не поступают в файл,
Я как раз и использую отдельный поток.
dred2kЯ себе делал класс лога через очередь, с пишущей ниткой,
:-) Ну у меня как раз такой и есть...
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38946416
dred2k
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
wadman:-) Ну у меня как раз такой и есть...
Я не уточнил - очередь данных (типа TList), а не системная очередь сообщений.
При интенсивной записи (в твоей реализации) код обработки,
пишущий в файл, "захлебнется" из-за накладных расходов записи -
не так это быстро, как ожидается.
Очередь сообщений "выбираться" не успеет.
PostMessage будет срабатывать не каждый раз, пойдут потери записей лога.
Для этой задачи, считаю, надежнее будет обычный threadsafe-буфер + нить записи.
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38946420
Фотография wadman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
dred2kВо как - тема-то вон какая...
Это про "15782361" .
Ой... Оказалось, там старая версия, а новее не выложил. Хотя с нагрузкой и эта версия отлично справляется.
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38946425
dred2k
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
В принципе, данный код легко модифицируется для защиты
от потерь - через буфер, опять же, с попыткой "досылки" сначала из него.
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38946432
dred2k
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
wadmanХотя с нагрузкой и эта версия отлично справляется.
Ну это да, как говорится - если "в мирное время".
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38946649
kealon(Ruslan)
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
wadmankealon(Ruslan), еще разок: у меня компилируется (и даже работает) под линуксом, ибо в fpc/lazarus есть модуль messages.
Unix mint 17.1
Код: plaintext
1.
Free Pascal Compiler version 2.6.2-8 [2014/01/22] for x86_64
Copyright (c) 1993-2012 by Florian Klaempfl and others

Код: plaintext
1.
wthread.pas(44,6) Fatal: Can not find unit Messages used by WThread. Check if package LCLBase is in the dependencies.
там стаб обычный, по умолчанию не цепляемый, под люниксом он не нужен собственно

PS: к dred2k присоединяюсь, такая штука рухнет под нагрузками (но в принципе почти любая очередь рухнет под нагрузками)
нужна блокировка при переполнении канала
и
Код: pascal
1.
 FQueue.Delete(0);

- всё же очень плохая вещь
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38946655
Фотография wadman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
kealon(Ruslan)
Код: plaintext
Free Pascal Compiler version 2.6.2-8 [2014/01/22] for x86_64

FPC 2.6.4 и последний лазарус. Компилируется и работает.
kealon(Ruslan)к dred2k присоединяюсь, такая штука рухнет под нагрузками
Сэмулируй нагрузку, под которой грохнется. А то при опросе пары сотен штук устройств с киданием десятков сообщений в секунду почему-то не падает.

Мне интересны факты, а не теория.
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38946676
dred2k
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
wadmanМне интересны факты, а не теория.
При использовании обсуждаемой реализации объективно
нет возможности гарантировать, что записи лога не начнут
пропадать по причине переполнения оконной очереди сообщений.
Вот тебе и факт.
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38946803
Фотография wadman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
dred2kВот тебе и факт.
Это не факт, а философия. Из товарища kealon(Ruslan) вытягивал несколько дней факт, почему у него не компилируется. Ну да ладно. Пока логирование в другом потоке не могу выложить, там несколько для меня специфичных вещей. Чуть позже, когда руки дойдут. Либо перепишу на основе wthread. Спасибо за обсуждение. :)
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38947197
kealon(Ruslan)
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
wadman, вроде нормальным английским языком, ошибка компиляции
wthread.pas(44,6) Fatal: Can not find unit Messages used by WThread. Check if package LCLBase is in the dependencies.
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38947218
Фотография wadman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
kealon(Ruslan)вроде нормальным английским языком, ошибка компиляции
Обнови лазарус.
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38947769
kealon(Ruslan)
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
wadmankealon(Ruslan)вроде нормальным английским языком, ошибка компиляции
Обнови лазарус.
Check if package LCLBase is in the dependencies

PS: вот не знаю что ты упираешься, тем более под люниксом эти константы оттуда не нужны
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38948142
Фотография wadman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
kealon(Ruslan)PS: вот не знаю что ты упираешься, тем более под люниксом эти константы оттуда не нужны
Есть этот модуль в 2.6.4., но ты одолел меня. Убрал совсем, нет в нем нужды (нужен был только для wm_user). :)
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #38948165
Фотография wadman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
wadman(нужен был только для wm_user)
Соврал, оттуда еще нужен TMessage... В общем учел, спасибо. В следующий раз messages будет вынесен куда следует.
...
Рейтинг: 0 / 0
25 сообщений из 469, страница 9 из 19
Форумы / Delphi [игнор отключен] [закрыт для гостей] / Шаблон класса для работы с потоком (WThread, Thread)
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


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