powered by simpleCommunicator - 2.0.61     © 2026 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / Delphi [игнор отключен] [закрыт для гостей] / Шаблон класса для работы с потоком (WThread, Thread)
25 сообщений из 469, страница 12 из 19
Шаблон класса для работы с потоком (WThread, Thread)
    #39021361
dred2k
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
2wadman
Помнится, мы с тобой уже пересекались по этой теме.
Глянь со стороны на модуль (D2010 >). Если что-то в комментах упустил - поясню.
Нужен внешний _конструктивный_ взгляд (ты по этой теме ближе, так что тебе адресую).
Ну, и все присоединяйтесь, разумеется.
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #39021362
dred2k
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Для затравки: DoCreate излишен, достаточно виртуального конструктора.
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #39021426
Фотография wadman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Беглым взглядом не совсем уловил сути: сендер создается в другом потоке, который создается иными средствами (руками, а не сендером)? То есть это продвинутый обработчик очереди с объектами?

Ну и примеры-бы. Куда-ж без них?
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #39021514
dred2k
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
wadman, в общем случае,
по месту использования - создаем экземпляр, объекты приходят по Post. Все.
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #39021532
dred2k
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
wadmanсендер создается в другом потоке, который создается иными средствами (руками, а не сендером)? То есть это продвинутый обработчик очереди с объектами?
Это в буквальном смысле использование механизма PostMessage для прозрачной пересылки.
Со всеми плюсами (прозрачность) и минусами (очередь на самом деле конечна).
Если создать объект Sender-а в нитке и регулярно вызывать его ProcessMessages - события будут приходить и выполняться в контексте нитки (для главной нитки достаточно создания объекта, количество не важно - события приходят адресно).
Однако, главное и изначальное применение - создание экземпляра в любой дельфийской форме и дальнейшее использование "асинхронности" в различных операциях с передачей произвольных объектов. В той нитке, в которой создан объект (и в которой вызывается ProcessMessages - для главной нитки не нужно), в той и будут обрабатываться приходящие события.
wadmanНу и примеры-бы. Куда-ж без них?
Пока, к сожалению, без них.
Сделаю, нужны.
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #39021568
Фотография wadman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
dred2kСо всеми плюсами (прозрачность) и минусами (очередь на самом деле конечна).
Меня удивляет акцент на длину очереди. На моем примере: дохлый целерон, на нем потоков около 120-ти (по одному на чтение и на запись), работа с железками. Нет никаких затыков и даже речи о десятках сообщений в очереди.

Хотя, это скорее вопрос архитектуры. Если криво написать, то можно нарваться на любые логические и физические лимиты.
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #39021581
dred2k
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
wadman, акцент он не от "красного" словца.
Вот представь ситуацию:
1. Нитка получает некие данные и постит их в окно для визуализации. Важно, чтобы "все дошло".
2. Вполне себе штатно вызывается некий внешний вызов (к примеру, экспорт TcxGrid-а, который, к сожалению, ничего "не знает кроме себя") - все, события благополучно не обрабатываются и копятся в очереди, в общем случае - бесконтрольно.
И таких ситуаций - хватает.
Я считаю, что в общем случае следует использовать надежную схему "очередь событий с хранением" + событие "есть данные в очереди". Ну, а в частных и аккуратно - можно и очередь событий Windows, "как она есть".
Как-то так.
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #39021587
dred2k
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
wadmanХотя, это скорее вопрос архитектуры. Если криво написать, то можно нарваться на любые логические и физические лимиты.
Согласен. В "конце концов" все упрется в правильность подхода.
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #39157161
Фотография wadman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Из изменений помню только, что лог теперь не дергает закрытие/открытие файла, если в очереди есть сообщения, что ускоряет вывод лога при большом потоке данных.
wthread.pas
Код: 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.
698.
699.
700.
701.
702.
703.
704.
705.
706.
707.
708.
709.
710.
711.
712.
713.
714.
715.
716.
717.
718.
719.
720.
721.
722.
723.
724.
725.
726.
727.
728.
729.
730.
731.
732.
733.
734.
735.
736.
737.
738.
739.
740.
741.
742.
743.
744.
745.
746.
747.
748.
749.
750.
751.
752.
753.
754.
755.
756.
757.
758.
759.
760.
761.
762.
763.
764.
765.
766.
767.
768.
769.
770.
771.
772.
773.
774.
775.
776.
777.
778.
779.
780.
781.
782.
783.
784.
785.
786.
787.
788.
789.
790.
791.
792.
793.
794.
795.
796.
797.
798.
799.
800.
801.
802.
803.
804.
805.
806.
807.
808.
809.
810.
811.
812.
813.
814.
815.
816.
817.
818.
819.
820.
821.
822.
823.
824.
825.
826.
827.
828.
829.
830.
831.
832.
833.
834.
835.
836.
837.
838.
839.
840.
841.
842.
843.
844.
845.
846.
847.
848.
849.
850.
851.
852.
853.
854.
855.
856.
857.
858.
859.
860.
861.
862.
863.
864.
865.
866.
867.
868.
869.
870.
871.
872.
873.
874.
875.
876.
877.
878.
879.
880.
881.
882.
883.
884.
885.
886.
887.
888.
889.
890.
891.
892.
893.
894.
895.
896.
897.
898.
899.
900.
901.
902.
903.
904.
905.
906.
907.
908.
909.
910.
911.
912.
913.
914.
915.
916.
917.
918.
919.
920.
921.
922.
923.
924.
925.
926.
927.
928.
929.
930.
931.
932.
933.
934.
935.
936.
937.
938.
939.
940.
941.
942.
943.
944.
945.
946.
947.
unit wthread;
// модуль для работы с доп.потоками Delphi&Lazarus (win&wince&*nix)
// позволяет "общаться" дополнительному и основному потокам посредством очереди сообщений
// (c) wadman 2013-2016, версия от 15.01.2016
//
// использование:
// 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}
// добавлять отладочную информацию с использованием модуля wlog
// для сборки демки необходимо включить (убрать точку)
{.$DEFINE WTHREAD_DEBUG_LOG}

interface

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

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

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

    TWEvent = class(TEvent)
    protected
        FWHandle: THandle;
    public
        constructor Create; overload;
        destructor Destroy; override;
        procedure WSetEvent;
        procedure WResetEvent;
        function WWaitFor(const TimeOut: Cardinal): TWaitResult;
    end;


    TWThread = class;

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

    { TWThread }

    TWThread = class(TThread)
    private
        FOwnerWThread: TWThread;
        {$IFDEF FPC}
        FQueue: TQueue;
        FSection: TCriticalSection;
        FMessageEvent: TWEvent;
        FGUIThread: TThread;
        {$ELSE}
        FQueueReady: boolean;
        FQueueMessages: array of TThreadMessage;
        FToolWindow: THandle;
        hCloseEvent: THandle;
        {$ENDIF}
        FOnThreadReceiveMessage: TWThreadReceiveMessage;
        FOnTimeOut: TWThreadTimeOut;
        FTimeOutIsDirect: boolean;
        FThreadName: string;
        {$IFDEF WTHREAD_DEBUG_LOG}
        FUseDebugLog: boolean;
        function intPostToLog(const Text: string): boolean; overload;
        function intPostToLog(const Text: string; const Level: TLogLevel): boolean; overload;
        {$ENDIF}
        function GetMsFromDateTime(const Value: TDateTime): Cardinal;
        procedure SetTimeOut(const Value: Cardinal);
        procedure FreeQueue;
    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);
            // отправка сообщения из этого потока другому потоку (см. OwnerWThread)
        procedure PostMessageToWThread(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;
        // проверяет, что выполнение идет в этом (доп) потоке
        function ItsMe: boolean;
            // процедура для очистки памяти в сообщениях, которые остались в очереди сообщений
            // при уничтожении потока
        procedure FreeMessage(const Msg: Word; const WParam: Word; const LParam: NativeInt); virtual;
    public
        constructor Create(const AOwnerThread: TWThread; const AThreadName: string
        {$IFDEF WTHREAD_DEBUG_LOG}
                ; AUseDebugLog: Boolean
        {$ENDIF}
            ); overload;
        constructor Create(const AOwnerThread: TWThread; const AThreadName: string; CreateSuspended
        {$IFDEF WTHREAD_DEBUG_LOG}
                , AUseDebugLog
        {$ENDIF}
                : Boolean); overload;
        constructor Create(CreateSuspended: boolean); overload;
        constructor Create(); 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;
            // доп.поток, которому отправляются сообщения с помощью PostMessageToWThread
        property OwnerWThread: TWThread read FOwnerWThread; // write FOwnerWThread;
            // событие, возникающее при превышении интервала ожидания
        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;
        {$IFDEF WTHREAD_DEBUG_LOG}
        property UseDebugLog: boolean read FUseDebugLog write FUseDebugLog default false;
        {$ENDIF}
        property ThreadName: string read FThreadName;
    end;

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

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

implementation

{$IFNDEF UNIX}
//uses Windows;
{$ENDIF}

const
    WM_INTERNAL_BASE    = WM_USER+$100;
    WM_TIMEOUT          = WM_INTERNAL_BASE+1;
    {$IFNDEF FPC}
    WM_THREAD_READY     = WM_INTERNAL_BASE+2;
    {$ENDIF}

    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
            {$HINTS OFF}
            p := GlobalLock(Result);
            {$HINTS ON}
            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
        {$HINTS OFF}
        ps := GlobalLock(P);
        {$HINTS ON}
        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}

constructor TWEvent.Create;
begin
    {$IFDEF WINCE}
    FWHandle := CreateEvent(nil, false, false, nil);
    {$ELSE}
    inherited Create(nil, false, false, '');
    {$ENDIF}
end;

destructor TWEvent.Destroy;
begin
    {$IFDEF WINCE}
    CloseHandle(FWHandle);
    {$ELSE}
    inherited;
    {$ENDIF}
end;

procedure TWEvent.WResetEvent;
begin
    {$IFDEF WINCE}
    Windows.ResetEvent(FWHandle);
    {$ELSE}
    inherited ResetEvent;
    {$ENDIF}
end;

procedure TWEvent.WSetEvent;
begin
    {$IFDEF WINCE}
    Windows.SetEvent(FWHandle);
    {$ELSE}
    inherited SetEvent;
    {$ENDIF}
end;

function TWEvent.WWaitFor(const TimeOut: Cardinal): TWaitResult;
{$IFDEF WINCE}
var
    dw: DWord;
begin
    dw := WaitForSingleObject(FWHandle, TimeOut);
    case dw of
        WAIT_ABANDONED: result := wrAbandoned;
        WAIT_OBJECT_0: result := wrSignaled;
        WAIT_TIMEOUT: result := wrTimeout;
    else
        Result := wrError;
    end;
{$ELSE}
begin
    result := WaitFor(TimeOut);
{$ENDIF}
end;

{$IFDEF FPC}
type

    { TGUIThread }

    TGUIThread = class(TThread)
    private
        FMessageEvent: TWEvent;
        FTimeOut: Boolean;
        FOwner: TWThread;
        FQueue: TQueue;
        FSection: TCriticalSection;
        FCurrentMessage: TThreadMessage;
        procedure FreeQueue;
    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 TGUIThread.FreeQueue;
var PMsg: PThreadMessage;
begin
    while FQueue.Count > 0 do begin
        PMsg := FQueue.Pop;
        FOwner.FreeMessage(PMsg^.Message, PMsg^.WParam, PMsg^.LParam);
    end;
end;

procedure TGUIThread.Execute;
var Message: PThreadMessage;
    wr: TWaitResult;
begin
    while not Terminated do begin
        wr := FMessageEvent.WWaitFor(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_WTHREAD_BASE)
                    and(FCurrentMessage.Message <= WM_WTHREAD_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 := TWEvent.Create;
    FQueue := TQueue.Create;
    FSection := TCriticalSection.Create;
    FOwner := AOwner;
end;

destructor TGUIThread.Destroy;
begin
    FreeQueue;
    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.WSetEvent;
end;

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

{ TWThread }

constructor TWThread.Create(const AOwnerThread: TWThread; const AThreadName: string; CreateSuspended
        {$IFDEF WTHREAD_DEBUG_LOG}
        , AUseDebugLog
        {$ENDIF}
        : Boolean);
begin
    inherited Create(CreateSuspended);
    FOwnerWThread := AOwnerThread;
    {$IFDEF WTHREAD_DEBUG_LOG}
    FUseDebugLog := AUseDebugLog;
    {$ENDIF}
    if Length(AThreadName) = 0 then
        FThreadName := 'Thread'
    else
        FThreadName := AThreadName;
    {$IFDEF FPC}
    FQueue := TQueue.Create;
    FMessageEvent := TWEvent.Create;
    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(const AOwnerThread: TWThread; const AThreadName: string
    {$IFDEF WTHREAD_DEBUG_LOG}
    ; AUseDebugLog: Boolean
    {$ENDIF}
    );
begin
    Create(AOwnerThread, AThreadName, false
        {$IFDEF WTHREAD_DEBUG_LOG}
        , AUseDebugLog
        {$ENDIF}
        );
end;

constructor TWThread.Create(CreateSuspended: boolean);
begin
    Create(nil, '', CreateSuspended
    {$IFDEF WTHREAD_DEBUG_LOG}
    , false
    {$ENDIF}
    );
end;

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

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

procedure TWThread.DirectTimeOut;
begin
    // override
end;

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

function TWThread.ItsMe: boolean;
begin
    result := GetCurrentThreadId = ThreadID;
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.WSetEvent;
    {$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;
    {$IFDEF WTHREAD_DEBUG_LOG}
    intPostToLog(Format('send to me msg: %d (0x%0:x).', [Msg]), WLL_EXTRA);
    {$ENDIF}
    FMessageEvent.WSetEvent;
    result := true;
{$ELSE}
begin
    if FQueueReady then begin
        // если очередь сообщений создана и поток инициализирован, то сразу отправляем
        result := (not Suspended)and(PostThreadMessage(ThreadID, Msg, wParam, lParam));
        {$IFDEF WTHREAD_DEBUG_LOG}
        intPostToLog(Format('send to me msg: %d (0x%0:x).', [Msg]), WLL_EXTRA);
        {$ENDIF}
    end else begin
        // иначе кэшируем
        SetLength(FQueueMessages, Length(FQueueMessages)+1);
        FQueueMessages[High(FQueueMessages)].Message := Msg;
        FQueueMessages[High(FQueueMessages)].WParam := WParam;
        FQueueMessages[High(FQueueMessages)].LParam := LParam;
        {$IFDEF WTHREAD_DEBUG_LOG}
        intPostToLog(Format('cached to me msg: %d (0x%0:x).', [Msg]), WLL_EXTRA);
        {$ENDIF}
        result := True;
    end;
{$ENDIF}
end;

{$IFDEF WTHREAD_DEBUG_LOG}
{$B-}
function TWThread.intPostToLog(const Text: string): boolean;
begin
    result := intPostToLog(Text, WLL_NORMAL);
end;

function TWThread.intPostToLog(const Text: string; const Level: TLogLevel): boolean;
begin
    result := FUseDebugLog and PostToLog(Format('%s(%d) : %s', [FThreadName, Handle, Text]), Level);
end;
{$ENDIF}

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.WSetEvent;
    end;
{$ELSE}
    if FTimeOut <> Value then begin
        if (Value = 0) then
            FTimeOut := INFINITE
        else
            FTimeOut := Value;
        if (not Suspended)and(not ItsMe) then
            PostThreadMessage(ThreadID, WM_TIMEOUT, Value, 0);
    end;
{$ENDIF}
end;

procedure TWThread.FreeQueue;
{$IFDEF FPC}
var PMsg: PThreadMessage;
begin
    while FQueue.Count > 0 do begin
        PMsg := FQueue.Pop;
        FreeMessage(PMsg^.Message, PMsg^.WParam, PMsg^.LParam);
    end;
{$ELSE}
begin
{$ENDIF}
end;

procedure TWThread.Execute;
var
    internalTimeout, ms: Cardinal;
    busy: TDateTime;
{$IFDEF FPC}
    Message: PThreadMessage;
    wr: TWaitResult;
begin
    {$IFDEF WTHREAD_DEBUG_LOG}
    intPostToLog('started.');
    {$ENDIF}
    InitThread;
    internalTimeout := TimeOut;
    busy := 0;
    {$IFDEF WTHREAD_DEBUG_LOG}
    intPostToLog('main loop started.', WLL_EXTRA);
    {$ENDIF}
    while not Terminated do begin
        wr := FMessageEvent.WWaitFor(internalTimeout);
        busy := Now;
        if not Terminated then
            case WR of
                wrSignaled: begin
                    {$IFDEF WTHREAD_DEBUG_LOG}
                    intPostToLog(Format('got signal (queue cnt: %d).', [FQueue.Count]), WLL_EXTRA);
                    {$ENDIF}
                    while FQueue.Count > 0 do begin
                        FSection.Enter;
                        Message := FQueue.Pop;
                        FSection.Leave;
                        if (Message^.message >= WM_WTHREAD_BASE)
                            and (Message^.Message <= WM_WTHREAD_MAX) then begin
                                {$IFDEF WTHREAD_DEBUG_LOG}
                                intPostToLog(Format('dispatch msg %d (0x%0:x).', [Message^.Message]), WLL_EXTRA);
                                {$ENDIF}
                                Dispatch(Message^);
                            end else if (Message^.message >= WM_INTERNAL_BASE)
                                and (Message^.Message <= WM_WTHREAD_BASE) then begin // внутренние сообщения
                            end;
                        FreeMem(Message);
                    end;
                end;
                wrTimeout: begin
                    internalTimeout := TimeOut;
                    if FTimeOutIsDirect then begin
                        {$IFDEF WTHREAD_DEBUG_LOG}
                        intPostToLog('DirectTimeOut.', WLL_EXTRA);
                        {$ENDIF}
                        DirectTimeOut;
                    end else begin
                        {$IFDEF WTHREAD_DEBUG_LOG}
                        intPostToLog('Sent TimeOut.', WLL_EXTRA);
                        {$ENDIF}
                        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
    // следующая строка должна быть всегда первой в процедуре, т.к. запускает очередь сообщений для потока
    {$HINTS OFF}
    PeekMessage(msg, THandle(-1), WM_USER, WM_USER, PM_NOREMOVE);
    {$HINTS ON}
    {$IFDEF WTHREAD_DEBUG_LOG}
    intPostToLog('main loop started.', WLL_EXTRA);
    {$ENDIF}
    internalTimeout := TimeOut;
    busy := 0;
    InitThread;
    HandlesToWaitFor[0] := hCloseEvent;
    PostMessageFromThread(WM_THREAD_READY, 0, 0);

    while not Terminated do begin
          if (not PeekMessage(msg, 0, 0, 0, PM_REMOVE)) then begin
            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
                        {$IFDEF WTHREAD_DEBUG_LOG}
                        intPostToLog('DirectTimeOut.', WLL_EXTRA);
                        {$ENDIF}
                        DirectTimeOut;
                    end else begin
                        {$IFDEF WTHREAD_DEBUG_LOG}
                        intPostToLog('Sent TimeOut.', WLL_EXTRA);
                        {$ENDIF}
                        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(msg.hwnd) then begin
            TranslateMessage(msg);
            DispatchMessage(msg);
        end else case msg.message of
            WM_TIMEOUT: begin
                // изменен таймаут
                FTimeOut := msg.wParam;
                busy := 0;
            end;
            WM_WTHREAD_BASE..WM_WTHREAD_MAX: begin
                message.Message := Msg.message;
                message.WParam := Msg.wParam;
                message.LParam := Msg.lParam;
                {$IFDEF WTHREAD_DEBUG_LOG}
                intPostToLog(Format('dispatch msg %d (0x%0:x).', [Message.Message]), WLL_EXTRA);
                {$ENDIF}
                // ищем и вызываем 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}
{$IFDEF WTHREAD_DEBUG_LOG}
    intPostToLog('main loop stoped.', WLL_EXTRA);
    {$ENDIF}
    DoneThread;
    {$IFDEF WTHREAD_DEBUG_LOG}
    intPostToLog('stoped.', WLL_EXTRA);
    {$ENDIF}
end;

// отправка сообщения из этого потока для вызова обработчика OnThreadReceiveMessage
procedure TWThread.PostMessageFromThread(const Msg: Word; const WParam: Word; const LParam: NativeInt);
begin
{$IFDEF FPC}
    if Assigned(FGUIThread) then begin
        TGUIThread(FGUIThread).PostMessage(Msg, WParam, LParam);
    {$IFDEF WTHREAD_DEBUG_LOG}
        intPostToLog(Format('send to receiver msg: %d (0x%0:x).', [Msg]), WLL_EXTRA);
    {$ENDIF}
    end
{$ELSE}
    if FToolWindow <> 0 then begin
        PostMessage(FToolWindow, Msg, WParam, LParam);
        {$IFDEF WTHREAD_DEBUG_LOG}
        intPostToLog(Format('send to receiver msg: %d (0x%0:x).', [Msg]), WLL_EXTRA);
        {$ENDIF}
    end
{$ENDIF}
    {$IFDEF WTHREAD_DEBUG_LOG}
    else
        intPostToLog(Format('cant send to receiver msg: %d (0x%0:x).', [Msg]), WLL_EXTRA);
    {$ENDIF}
end;

procedure TWThread.PostMessageToWThread(const Msg: Word; const WParam: Word; const LParam: NativeInt);
begin
    if Assigned(FOwnerWThread) then begin
        FOwnerWThread.PostToThreadMessage(Msg, WParam, LParam);
   {$IFDEF WTHREAD_DEBUG_LOG}
        intPostToLog(Format('send to %s(%d) msg: %d (0x%2:x).', [FOwnerWThread.FThreadName, FOwnerWThread.Handle, Msg]), WLL_EXTRA);
    end else begin
        intPostToLog(Format('cant send to other wthread msg: %d (0x%0:x).', [Msg]), WLL_EXTRA);
    {$ENDIF}
    end;
end;

procedure TWThread.StopThread;
begin
    {$IFDEF FPC}
    TGUIThread(FGUIThread).StopThread;
    Terminate;
    FMessageEvent.WSetEvent;
    {$ELSE}
    SetLength(FQueueMessages, 0);
    FreeWindow;
    Terminate;
    SetEvent(hCloseEvent);
    //SleepEx(1, false);
    //result := WaitForSingleObject(Handle, 10000) <> WAIT_TIMEOUT;
    {$ENDIF}
    {$IFDEF WTHREAD_DEBUG_LOG}
    intPostToLog('stop signal.');
    {$ENDIF}
end;

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

procedure TWThread.FreeMessage(const Msg: Word; const WParam: Word; const LParam: NativeInt);
begin
    // эту процедуру необходимо перекрыть для освобождения памяти, которая была выделена и
    // передана через очередь в этот или из этого потока (через GUIThread)
    // исп. в FreeQueue
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_WTHREAD_BASE..WM_WTHREAD_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.pas
Код: 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.
unit wlog;
// wlog (c) wadman, 2015-2016, 19.01.2016
// multithread safe logging

interface

// log level
type
    TLogLevel = (
        WLL_MINIMUM     = 1,
        WLL_NORMAL      = 2,
        WLL_MAXIMUM     = 3,
        WLL_EXTRA       = 4
    );

var // имя лог-файла (по ум. имя исполняемого файла с расширением log
    WLogFileName: string;
    // логирование включено (по ум. да)
    WLogEnabled: boolean;
    // очищать лог при каждом запуске (по ум. да)
    WLogClearOnStart: boolean;
    // уровень логирования по ум. см TLogLevel (по ум. WLL_NORMAL, т.е. MAXIMUM и EXTRA не будут логироваться)
    WLogLevel: TLogLevel;

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

implementation

uses Windows, SysUtils, WThread;

const
    WM_LOG      = WM_WTHREAD_BASE + 1;
    WM_ERASE    = WM_WTHREAD_BASE + 2;

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

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

var
    LogThread: TLogThread;
    FirstLine: boolean;
    log: TextFile;
    FileOpened: 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;

procedure EraseLog;
begin
    LogThread.PostToThreadMessage(WM_ERASE, 0, 0);
end;

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

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

function OpenLog: boolean;
begin
    result := false;
    try
        AssignFile(log, WLogFileName);
        if (FileExists(WLogFileName))and(not WLogClearOnStart) then begin
            Append(log);
            if FirstLine then begin
                WriteLn(log);
                FirstLine := false;
            end;
        end else begin
            WLogClearOnStart := false;
            Rewrite(log);
        end;
        result := true;
    finally

    end;
end;

function CloseLog: boolean;
begin
    result := false;
    try
        CloseFile(log);
        result := true;
    finally

    end;
end;

function AddToLog(const DT: TDateTime; const Text: string): boolean;

    function ClearText(const AText: string): String;
    var i: integer;
    begin
        result := AText;
        for I := 1 to Length(result) do
            if CharInSet(result[i], [#0..#31]) then
                result[i] := '.';
    end;

begin
    result := false;
    try
        WriteLn(log, Format('%s : %s', [FormatDateTime('dd.mm.yyyy hh:nn:ss.zzz', DT), ClearText(Text)]));
        result := true;
    finally
    end;
end;

{ TLogThread }

procedure TLogThread.WMLog(var Msg: TThreadMessage);
var p: PLogRecord;
    m: TMsg;
begin
{$HINTS OFF}
    p := PLogRecord(Msg.LParam);
{$HINTS ON}
    if (not FileOpened) then
        FileOpened := OpenLog;
    if FileOpened then
        AddToLog(p^.DT, FreeString(p^.PString));
    if (not PeekMessage(m, Handle, WM_LOG, WM_LOG, PM_NOREMOVE)) or Terminated then
        FileOpened := not CloseLog;
    FreeMem(p);
end;

procedure TLogThread.WMErase(var Msg: TThreadMessage);
var f: File;
begin
    if FileOpened then
        FileOpened := not CloseLog;
    try
        if (not FileOpened) and (FileExists(WLogFileName)) then begin
            AssignFile(f, WLogFileName);
            Erase(f);
        end;
    finally

    end;
end;

initialization
    FileOpened := false;
    WLogLevel := WLL_NORMAL;
    WLogEnabled := InitLogs;
    FirstLine := true;
    WLogClearOnStart := false;

finalization
    DoneLogs;

end.

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

Использую твой модуль в своем проекте. Работает пока без нареканий.
Небольшое предложение:
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
        constructor Create(const AOwnerThread: TWThread; const AThreadName: string
        {$IFDEF WTHREAD_DEBUG_LOG}
                ; AUseDebugLog: Boolean = false
        {$ENDIF}
            ); overload;
        constructor Create(const AOwnerThread: TWThread; CreateSuspended: Boolean; const AThreadName: string
        {$IFDEF WTHREAD_DEBUG_LOG}
                ; AUseDebugLog: Boolean = false
        {$ENDIF}
                ); overload;  


Чтобы не менять количество параметров в конструкторе, если включать wlog.
И еще не объясни назначение DoneThread. Код из него можно же написать в деструкторе.
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #39158164
Фотография wadman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
brick08Чтобы не менять количество параметров в конструкторе, если включать wlog.
wlog без проблем включается. Или ты WTHREAD_DEBUG_LOG используешь?
brick08И еще не объясни назначение DoneThread. Код из него можно же написать в деструкторе.
Код: pascal
1.
2.
3.
            // Две процедуры, которые выполняются в контексте потока. В начале и в конце.
        procedure InitThread; virtual;
        procedure DoneThread; virtual;
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #39158190
Фотография brick08
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
[quot wadman]brick08 Или ты WTHREAD_DEBUG_LOG используешь?
В том то и дело, что при тестах, то включал, то выключал директиву. Соответственно компилятор начинает ругаться на несовпадение кол-ва параметров в конструкторе. Так вот, чтобы избежать этого, просто поставить AUseDebugLog: Boolean = True в конструкторе.

А разве деструктор не выполняется в контексте?
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #39158210
Фотография wadman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
brick08В том то и дело, что при тестах, то включал, то выключал директиву.
Вынеси в wthread.inc и добавь строку в оба модуля {$i wthread.inc}
Код: pascal
1.
2.
3.
// добавлять отладочную информацию с использованием модуля wlog
// для сборки демки необходимо включить (убрать точку)
{$DEFINE WTHREAD_DEBUG_LOG}


в wlog.pas поправь
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
function InitLogs: boolean;
begin
    WLogFileName := ChangeFileExt(ParamStr(0), '.log');
    LogThread := TLogThread.Create(nil, 'LogThread',
{$IFDEF WTHREAD_DEBUG_LOG}
        false,
{$ENDIF}
    false
    );
    result := LongBool(LogThread);
end;


В следующий раз выложу уже с этими изменениями.


brick08А разве деструктор не выполняется в контексте?
Нет.
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #39158326
Фотография wadman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Восстановил совместимость с *никсами (убрал PeekMessage).
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #39303639
Фотография wadman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Добавил поддержку AffinityMask. Теперь можно раскидывать потоки по разным ядрам.
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #39304625
kealon(Ruslan)
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
wadman,

а не пробовали I/O Completion Ports для этого приспособить?
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #39304929
Фотография wadman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
kealon(Ruslan)а не пробовали I/O Completion Ports для этого приспособить?
Попробовал-бы, если-бы знал "зачем?" :)
А почитав https://habrahabr.ru/post/59282/ и вовсе растерялся...
Чем оно хорошо и в каких случаях?
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #39304988
kealon(Ruslan)
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
wadman,
ну фактически, как я понимаю, конкретно для реализации логера, можно писать сразу в файл асинхронным способом и в потоке обработки только подчищать ресурсы выделенных буферов
т.е. алгоритм такой:
выделяется блок памяти (условно строка)

вызывается операция записи в файл, в которую передаётся выделенный блок

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

Асинхронно - это без порядка, что, на мой взгляд, вредно для логирования, где порядок имеет первостепенное значение.
ну асинхронно здесь имеется ввиду "не ждать результата", порядок то будет соблюдаться
Хорошо, а плюс в чем, если сравнивать с моей реализацией?
Что у меня не хватает?
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #39305014
kealon(Ruslan)
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
wadmanХорошо, а плюс в чем, если сравнивать с моей реализацией?
Что у меня не хватает?
тут дело в том, что с излишком хватает
1. самое главное - очередь не придётся самостоятельно организовывать
2. возможно работать будет быстрее
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #39305018
Фотография wadman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
kealon(Ruslan)1. самое главное - очередь не придётся самостоятельно организовывать
И у меня очередь не своя. :)
kealon(Ruslan)2. возможно работать будет быстрее
Скорее всего нет.
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #39305022
kealon(Ruslan)
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
wadmanСкорее всего нет.
я бы не был так категоричен без тестов, потому и спросил пробовали или нет
...
Рейтинг: 0 / 0
Шаблон класса для работы с потоком (WThread, Thread)
    #39305027
Фотография wadman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
kealon(Ruslan)wadmanСкорее всего нет.
я бы не был так категоричен без тестов, потому и спросил пробовали или нет
Я-бы попробовал, если-бы мне сказали, что это лучше/быстрее/надежнее.
...
Рейтинг: 0 / 0
25 сообщений из 469, страница 12 из 19
Форумы / Delphi [игнор отключен] [закрыт для гостей] / Шаблон класса для работы с потоком (WThread, Thread)
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


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