mirror of
https://github.com/loginov-dmitry/multithread.git
synced 2025-02-20 07:58:22 +02:00
Добавлен пример SimpleLogger, исправление в разделе "Приоритеты потоков"
This commit is contained in:
parent
c9634cb1db
commit
37e3e74743
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
|
||||
*.dcu
|
||||
*.local
|
||||
*.identcache
|
161
CommonUtils/MTLogger.pas
Normal file
161
CommonUtils/MTLogger.pas
Normal file
@ -0,0 +1,161 @@
|
||||
unit MTLogger;
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
Windows, SysUtils, Classes, SyncObjs, MTUtils;
|
||||
|
||||
type
|
||||
TLoggerThread = class(TThread)
|
||||
private
|
||||
FLogFileName: string;
|
||||
Event: TEvent;
|
||||
CritSect: TCriticalSection; // Äëÿ çàùèòû ñïèñêà ñòðîê
|
||||
LogStrings: TStringList;
|
||||
protected
|
||||
procedure Execute; override;
|
||||
public
|
||||
constructor Create(LogFileName: string);
|
||||
destructor Destroy; override;
|
||||
procedure AddToLog(const Msg: string);
|
||||
end;
|
||||
|
||||
var
|
||||
DefLogger: TLoggerThread;
|
||||
AllowMessageBoxIfError: Boolean = False;
|
||||
|
||||
procedure CreateDefLogger(DefLogFileName: string);
|
||||
procedure FreeDefLogger;
|
||||
|
||||
// Çàïèñûâàåò çàäàííóþ ñòðîêó òåêñòà â óêàçàííûé ôàéë
|
||||
procedure WriteStringToTextFile(AFileName: string; Msg: string);
|
||||
|
||||
implementation
|
||||
|
||||
procedure CreateDefLogger(DefLogFileName: string);
|
||||
begin
|
||||
if DefLogger = nil then
|
||||
DefLogger := TLoggerThread.Create(DefLogFileName);
|
||||
end;
|
||||
|
||||
procedure FreeDefLogger;
|
||||
begin
|
||||
FreeAndNil(DefLogger);
|
||||
end;
|
||||
|
||||
procedure WriteStringToTextFile(AFileName: string; Msg: string);
|
||||
var
|
||||
AFile: TextFile;
|
||||
begin
|
||||
try
|
||||
// Îòêðûâàåì ôàéë ïðè êàæäîì äîáàâëåíèè ñòðîêè! Ïðè áîëüøîì îáúåìå çàïèñè
|
||||
// â ëîã ôàéë ýòî î÷åíü íåðàöèîíàëüíî!
|
||||
AssignFile(AFile, AFileName);
|
||||
if FileExists(AFileName) then
|
||||
Append(AFile)
|
||||
else
|
||||
Rewrite(AFile);
|
||||
Writeln(AFile, Msg);
|
||||
CloseFile(AFile);
|
||||
except
|
||||
on E: Exception do
|
||||
begin
|
||||
// Âíèìàíèå!  ðåàëüíîì ïðèëîæåíèè âûäà÷à ïîëüçîâàòåëþ ñîîáùåíèÿ îá îøèáêå
|
||||
// çàïèñè â ëîã-ôàéë íåäîïóñòèìà!
|
||||
if AllowMessageBoxIfError then
|
||||
ThreadShowMessageFmt('Îøèáêà ïðè çàïèñè â ôàéë [%s] ñòðîêè "%s": %s', [AFileName, Msg, E.Message]);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
{ TLoggerThread }
|
||||
|
||||
procedure TLoggerThread.AddToLog(const Msg: string);
|
||||
begin
|
||||
CritSect.Enter;
|
||||
try
|
||||
LogStrings.Add(Format('%s [P:%d T:%d] - %s', [FormatDateTime('dd.mm.yyyy hh:nn:ss.zzz', Now),
|
||||
GetCurrentProcessId, GetCurrentThreadId, Msg]));
|
||||
finally
|
||||
CritSect.Leave;
|
||||
end;
|
||||
Event.SetEvent;
|
||||
end;
|
||||
|
||||
constructor TLoggerThread.Create(LogFileName: string);
|
||||
const
|
||||
STATE_NONSIGNALED = FALSE;
|
||||
AUTO_RESET = FALSE;
|
||||
begin
|
||||
inherited Create(False);
|
||||
|
||||
// Ñîçäà¸ì îáúåêò "Event" â ñîñòîÿíèè "nonsignaled" è ïðîñèì, ÷òîáû
|
||||
// îí àâòîìàòè÷åñêè ïåðåõîäèë â ñîñòîÿíèå "nonsignaled" ïîñëå WaitFor
|
||||
Event := TEvent.Create(nil, AUTO_RESET, STATE_NONSIGNALED, '', False);
|
||||
|
||||
// Ñîçäà¸ì ñïèñîê ñòðîê LogStrings
|
||||
LogStrings := TStringList.Create;
|
||||
|
||||
// Ñîçäà¸ì êðèòè÷åñêóþ ñåêöèþ äëÿ çàùèòû ñïèñêà ñòðîê îò îäíîâðåìåííîãî
|
||||
// äîñòóïà èç íåñêîëüêèõ ïîòîêîâ
|
||||
CritSect := TCriticalSection.Create;
|
||||
|
||||
FLogFileName := LogFileName;
|
||||
end;
|
||||
|
||||
destructor TLoggerThread.Destroy;
|
||||
begin
|
||||
// Î÷åíü âàæíî, ÷òîáû âûçîâ Terminate áûë ðàíüøå âûçîâà SetEvent!
|
||||
Terminate; // 1. Âûñòàâëÿåì ôëàã Terminated
|
||||
Event.SetEvent; // 2. Ïåðåâîäèì Event â ñîñòîÿíèå SIGNALED
|
||||
inherited; // 3. Äîæèäàåìñÿ âûõîäà èç ìåòîäà Execute
|
||||
Event.Free; // 4. Óíè÷òîæàåì îáúåêò Event
|
||||
CritSect.Free;
|
||||
LogStrings.Free;
|
||||
end;
|
||||
|
||||
procedure TLoggerThread.Execute;
|
||||
var
|
||||
TmpList: TStringList;
|
||||
begin
|
||||
while True do
|
||||
begin
|
||||
// Çàâåðøàåì ðàáîòó ïîòîêà â òîì ñëó÷àå, åñëè ïîëüçîâàòåëü âûõîäèò èç ïðîãðàììû,
|
||||
// à ïîòîê óñïåë ñêèíóòü â ëîã-ôàéë âñå ñîîáùåíèÿ èç ñïèñêà LogStrings
|
||||
if Terminated and (LogStrings.Count = 0) then Exit;
|
||||
|
||||
// Îæèäàåì ïåðåêëþ÷åíèÿ îáúåêòà Event â ñîñòîÿíèå signaled, íî íå áîëåå 2õ ñåêóíä
|
||||
// Ïðè âûçîâå ìåòîäà TLoggerThread.AddToLog âûïîëíÿåòñÿ âûçîâ Event.SetEvent,
|
||||
// îäíàêî ëó÷øå ïåðåñòðàõîâàòüñÿ è íå äåëàòü îæèäàíèå áåñêîíå÷íûì
|
||||
Event.WaitFor(2000);
|
||||
|
||||
// Ïðîâåðÿåì ñâîéñòâî Count áåç êðèòè÷åñêîé ñåêöèè (ýòî áåçîïàñíî)
|
||||
if LogStrings.Count > 0 then
|
||||
begin
|
||||
TmpList := TStringList.Create;
|
||||
try
|
||||
// 1. Âõîäèì â êðèòè÷åñêóþ ñåêöèþ
|
||||
CritSect.Enter;
|
||||
try
|
||||
// 2. Êîïèðóåì âñå ñòðîêè èç LogStrings â TmpList
|
||||
TmpList.Assign(LogStrings);
|
||||
// 3. Î÷èùàåì ñïèñîê LogStrings
|
||||
LogStrings.Clear;
|
||||
finally
|
||||
// 4. Âûõîäèì èç êðèòè÷åñêîé ñåêöèè
|
||||
CritSect.Leave;
|
||||
end;
|
||||
|
||||
// Ñïèñîê TmpList ÿâëÿåòñÿ ëîêàëüíûì, ïîýòîìó åãî íå íóæíî çàùèùàòü
|
||||
// êðèòè÷åñêîé ñåêöèåé.
|
||||
|
||||
// 5. Çà îäíî äåéñòâèå çàïèñûâàåì âñå ñòðîêè èç ñïèñêà TmpList â ëîã-ôàéë
|
||||
WriteStringToTextFile(FLogFileName, Trim(TmpList.Text));
|
||||
finally
|
||||
TmpList.Free;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
end.
|
17
ExSync/ExEvent/SimpleLogger/SimpleLogger.dpr
Normal file
17
ExSync/ExEvent/SimpleLogger/SimpleLogger.dpr
Normal file
@ -0,0 +1,17 @@
|
||||
program SimpleLogger;
|
||||
|
||||
uses
|
||||
Forms,
|
||||
SimpleLoggerUnit in 'SimpleLoggerUnit.pas' {DemoLoggerForm},
|
||||
MTUtils in '..\..\..\CommonUtils\MTUtils.pas',
|
||||
TimeIntervals in '..\..\..\CommonUtils\TimeIntervals.pas',
|
||||
MTLogger in '..\..\..\CommonUtils\MTLogger.pas';
|
||||
|
||||
{$R *.res}
|
||||
|
||||
begin
|
||||
Application.Initialize;
|
||||
Application.MainFormOnTaskbar := True;
|
||||
Application.CreateForm(TDemoLoggerForm, DemoLoggerForm);
|
||||
Application.Run;
|
||||
end.
|
39
ExSync/ExEvent/SimpleLogger/SimpleLogger.dproj
Normal file
39
ExSync/ExEvent/SimpleLogger/SimpleLogger.dproj
Normal file
@ -0,0 +1,39 @@
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<ProjectGuid>{7ad5f485-3983-41b2-9088-147054b7fa84}</ProjectGuid>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<DCC_DCCCompiler>DCC32</DCC_DCCCompiler>
|
||||
<DCC_DependencyCheckOutputName>SimpleLogger.exe</DCC_DependencyCheckOutputName>
|
||||
<MainSource>SimpleLogger.dpr</MainSource>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<Version>7.0</Version>
|
||||
<DCC_DebugInformation>False</DCC_DebugInformation>
|
||||
<DCC_LocalDebugSymbols>False</DCC_LocalDebugSymbols>
|
||||
<DCC_SymbolReferenceInfo>0</DCC_SymbolReferenceInfo>
|
||||
<DCC_Define>RELEASE</DCC_Define>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<Version>7.0</Version>
|
||||
<DCC_Define>DEBUG</DCC_Define>
|
||||
</PropertyGroup>
|
||||
<ProjectExtensions>
|
||||
<Borland.Personality>Delphi.Personality</Borland.Personality>
|
||||
<Borland.ProjectType />
|
||||
<BorlandProject>
|
||||
<BorlandProject><Delphi.Personality><Parameters><Parameters Name="UseLauncher">False</Parameters><Parameters Name="LoadAllSymbols">True</Parameters><Parameters Name="LoadUnspecifiedSymbols">False</Parameters></Parameters><VersionInfo><VersionInfo Name="IncludeVerInfo">False</VersionInfo><VersionInfo Name="AutoIncBuild">False</VersionInfo><VersionInfo Name="MajorVer">1</VersionInfo><VersionInfo Name="MinorVer">0</VersionInfo><VersionInfo Name="Release">0</VersionInfo><VersionInfo Name="Build">0</VersionInfo><VersionInfo Name="Debug">False</VersionInfo><VersionInfo Name="PreRelease">False</VersionInfo><VersionInfo Name="Special">False</VersionInfo><VersionInfo Name="Private">False</VersionInfo><VersionInfo Name="DLL">False</VersionInfo><VersionInfo Name="Locale">1049</VersionInfo><VersionInfo Name="CodePage">1251</VersionInfo></VersionInfo><VersionInfoKeys><VersionInfoKeys Name="CompanyName"></VersionInfoKeys><VersionInfoKeys Name="FileDescription"></VersionInfoKeys><VersionInfoKeys Name="FileVersion">1.0.0.0</VersionInfoKeys><VersionInfoKeys Name="InternalName"></VersionInfoKeys><VersionInfoKeys Name="LegalCopyright"></VersionInfoKeys><VersionInfoKeys Name="LegalTrademarks"></VersionInfoKeys><VersionInfoKeys Name="OriginalFilename"></VersionInfoKeys><VersionInfoKeys Name="ProductName"></VersionInfoKeys><VersionInfoKeys Name="ProductVersion">1.0.0.0</VersionInfoKeys><VersionInfoKeys Name="Comments"></VersionInfoKeys></VersionInfoKeys><Source><Source Name="MainSource">SimpleLogger.dpr</Source></Source></Delphi.Personality></BorlandProject></BorlandProject>
|
||||
</ProjectExtensions>
|
||||
<Import Project="$(MSBuildBinPath)\Borland.Delphi.Targets" />
|
||||
<ItemGroup>
|
||||
<DelphiCompile Include="SimpleLogger.dpr">
|
||||
<MainSource>MainSource</MainSource>
|
||||
</DelphiCompile>
|
||||
<DCCReference Include="..\..\..\CommonUtils\MTLogger.pas" />
|
||||
<DCCReference Include="..\..\..\CommonUtils\MTUtils.pas" />
|
||||
<DCCReference Include="..\..\..\CommonUtils\TimeIntervals.pas" />
|
||||
<DCCReference Include="SimpleLoggerUnit.pas">
|
||||
<Form>DemoLoggerForm</Form>
|
||||
</DCCReference>
|
||||
</ItemGroup>
|
||||
</Project>
|
BIN
ExSync/ExEvent/SimpleLogger/SimpleLogger.res
Normal file
BIN
ExSync/ExEvent/SimpleLogger/SimpleLogger.res
Normal file
Binary file not shown.
90
ExSync/ExEvent/SimpleLogger/SimpleLoggerUnit.dfm
Normal file
90
ExSync/ExEvent/SimpleLogger/SimpleLoggerUnit.dfm
Normal file
@ -0,0 +1,90 @@
|
||||
object DemoLoggerForm: TDemoLoggerForm
|
||||
Left = 0
|
||||
Top = 0
|
||||
Caption = #1044#1077#1084#1086#1085#1089#1090#1088#1072#1094#1080#1103' '#1079#1072#1087#1080#1089#1080' '#1074' '#1083#1086#1075' '#1095#1077#1088#1077#1079' '#1076#1086#1087#1086#1083#1085#1080#1090#1077#1083#1100#1085#1099#1081' '#1087#1086#1090#1086#1082
|
||||
ClientHeight = 288
|
||||
ClientWidth = 635
|
||||
Color = clBtnFace
|
||||
Font.Charset = DEFAULT_CHARSET
|
||||
Font.Color = clWindowText
|
||||
Font.Height = -13
|
||||
Font.Name = 'Tahoma'
|
||||
Font.Style = []
|
||||
OldCreateOrder = False
|
||||
OnCreate = FormCreate
|
||||
OnDestroy = FormDestroy
|
||||
PixelsPerInch = 96
|
||||
TextHeight = 16
|
||||
object Label1: TLabel
|
||||
Left = 16
|
||||
Top = 24
|
||||
Width = 132
|
||||
Height = 16
|
||||
Caption = #1044#1086#1073#1072#1074#1080#1090#1100' '#1089#1086#1086#1073#1097#1077#1085#1080#1081':'
|
||||
end
|
||||
object Label2: TLabel
|
||||
Left = 24
|
||||
Top = 96
|
||||
Width = 457
|
||||
Height = 32
|
||||
Caption =
|
||||
#1054#1089#1090#1086#1088#1086#1078#1085#1086'! '#1045#1089#1083#1080' '#1042#1099' '#1080#1089#1087#1086#1083#1100#1079#1091#1077#1090#1077' SSD, '#1090#1086' '#1087#1086#1089#1090#1088#1086#1095#1085#1072#1103' '#1079#1072#1087#1080#1089#1100' 10 '#1090#1099#1089'.' +
|
||||
' '#1089#1090#1088#1086#1082' '#1074#13#10#1083#1086#1075'-'#1092#1072#1081#1083' '#1084#1086#1078#1077#1090' '#1079#1072#1085#1103#1090#1100' '#1086#1082#1086#1083#1086' '#1084#1080#1085#1091#1090#1099', '#1074' '#1079#1072#1074#1080#1089#1080#1084#1086#1089#1090#1080' '#1086#1090' '#1084 +
|
||||
#1086#1076#1077#1083#1080' SSD.'
|
||||
Font.Charset = DEFAULT_CHARSET
|
||||
Font.Color = clBlue
|
||||
Font.Height = -13
|
||||
Font.Name = 'Tahoma'
|
||||
Font.Style = []
|
||||
ParentFont = False
|
||||
end
|
||||
object Label3: TLabel
|
||||
Left = 24
|
||||
Top = 226
|
||||
Width = 486
|
||||
Height = 32
|
||||
Caption =
|
||||
#1055#1088#1080' '#1079#1072#1087#1080#1089#1080' '#1074' '#1083#1086#1075' '#1095#1077#1088#1077#1079' '#1076#1086#1087'. '#1087#1086#1090#1086#1082' '#1073#1091#1076#1077#1090' '#1084#1086#1084#1077#1085#1090#1072#1083#1100#1085#1086' '#1079#1072#1087#1080#1089#1099#1074#1072#1090#1100#1089#1103 +
|
||||
' '#1087#1088#1072#1082#1090#1080#1095#1077#1089#1082#1080#13#10#1083#1102#1073#1086#1081' '#1086#1073#1098#1105#1084' '#1080#1085#1092#1086#1088#1084#1072#1094#1080#1080', '#1076#1072#1078#1077' 1 '#1084#1083#1085' '#1089#1090#1088#1086#1082'!'
|
||||
Font.Charset = DEFAULT_CHARSET
|
||||
Font.Color = clBlue
|
||||
Font.Height = -13
|
||||
Font.Name = 'Tahoma'
|
||||
Font.Style = []
|
||||
ParentFont = False
|
||||
end
|
||||
object Button1: TButton
|
||||
Left = 16
|
||||
Top = 67
|
||||
Width = 425
|
||||
Height = 25
|
||||
Caption = #1044#1086#1073#1072#1074#1080#1090#1100' '#1089#1086#1086#1073#1097#1077#1085#1080#1103' '#1074' '#1083#1086#1075'-'#1092#1072#1081#1083' '#1073#1077#1079' '#1076#1086#1087#1086#1083#1085#1080#1090#1077#1083#1100#1085#1086#1075#1086' '#1087#1086#1090#1086#1082#1072
|
||||
TabOrder = 0
|
||||
OnClick = Button1Click
|
||||
end
|
||||
object Edit1: TEdit
|
||||
Left = 154
|
||||
Top = 21
|
||||
Width = 87
|
||||
Height = 24
|
||||
TabOrder = 1
|
||||
Text = '10000'
|
||||
end
|
||||
object Button2: TButton
|
||||
Left = 16
|
||||
Top = 195
|
||||
Width = 425
|
||||
Height = 25
|
||||
Caption = #1044#1086#1073#1072#1074#1080#1090#1100' '#1089#1086#1086#1073#1097#1077#1085#1080#1103' '#1074' '#1083#1086#1075'-'#1092#1072#1081#1083' '#1095#1077#1088#1077#1079' '#1076#1086#1087#1086#1083#1085#1080#1090#1077#1083#1100#1085#1099#1081' '#1087#1086#1090#1086#1082
|
||||
TabOrder = 2
|
||||
OnClick = Button2Click
|
||||
end
|
||||
object cbCloseApp: TCheckBox
|
||||
Left = 456
|
||||
Top = 200
|
||||
Width = 145
|
||||
Height = 17
|
||||
Caption = #1047#1072#1082#1088#1099#1090#1100' '#1087#1088#1086#1075#1088#1072#1084#1084#1091
|
||||
TabOrder = 3
|
||||
end
|
||||
end
|
75
ExSync/ExEvent/SimpleLogger/SimpleLoggerUnit.pas
Normal file
75
ExSync/ExEvent/SimpleLogger/SimpleLoggerUnit.pas
Normal file
@ -0,0 +1,75 @@
|
||||
unit SimpleLoggerUnit;
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
|
||||
Dialogs, StdCtrls, SyncObjs, MTUtils, TimeIntervals, MTLogger;
|
||||
|
||||
type
|
||||
TDemoLoggerForm = class(TForm)
|
||||
Button1: TButton;
|
||||
Label1: TLabel;
|
||||
Edit1: TEdit;
|
||||
Button2: TButton;
|
||||
Label2: TLabel;
|
||||
Label3: TLabel;
|
||||
cbCloseApp: TCheckBox;
|
||||
procedure FormCreate(Sender: TObject);
|
||||
procedure FormDestroy(Sender: TObject);
|
||||
procedure Button1Click(Sender: TObject);
|
||||
procedure Button2Click(Sender: TObject);
|
||||
private
|
||||
{ Private declarations }
|
||||
public
|
||||
{ Public declarations }
|
||||
end;
|
||||
|
||||
var
|
||||
DemoLoggerForm: TDemoLoggerForm;
|
||||
|
||||
implementation
|
||||
|
||||
{$R *.dfm}
|
||||
|
||||
procedure TDemoLoggerForm.Button1Click(Sender: TObject);
|
||||
var
|
||||
ti: TTimeInterval;
|
||||
I: Integer;
|
||||
AFileName: string;
|
||||
begin
|
||||
AFileName := ExtractFilePath(Application.ExeName) + 'EventsNoThread.log';
|
||||
ti.Start;
|
||||
for I := 1 to StrToInt(Edit1.Text) do
|
||||
WriteStringToTextFile(AFileName, Format('%s [P:%d T:%d] - Ñîáûòèå ¹%d',
|
||||
[FormatDateTime('dd.mm.yyyy hh:nn:ss.zzz', Now), GetCurrentProcessId, GetCurrentThreadId, I]));
|
||||
|
||||
ShowMessageFmt('Âðåìÿ äîáàâëåíèÿ ñîáûòèé â ëîã-ôàéë: %d ìñ', [ti.ElapsedMilliseconds]);
|
||||
end;
|
||||
|
||||
procedure TDemoLoggerForm.Button2Click(Sender: TObject);
|
||||
var
|
||||
ti: TTimeInterval;
|
||||
I: Integer;
|
||||
begin
|
||||
ti.Start;
|
||||
for I := 1 to StrToInt(Edit1.Text) do
|
||||
DefLogger.AddToLog(Format('Ñîáûòèå ¹%d', [I]));
|
||||
if cbCloseApp.Checked then
|
||||
Close
|
||||
else
|
||||
ShowMessageFmt('Âðåìÿ äîáàâëåíèÿ ñîáûòèé â ëîã-ôàéë: %d ìñ', [ti.ElapsedMilliseconds]);
|
||||
end;
|
||||
|
||||
procedure TDemoLoggerForm.FormCreate(Sender: TObject);
|
||||
begin
|
||||
AllowMessageBoxIfError := True; // Òîëüêî â äåìîíñòðàöèîííûõ öåëÿõ!!!
|
||||
CreateDefLogger(ExtractFilePath(Application.ExeName) + 'EventsInThread.log');
|
||||
end;
|
||||
|
||||
procedure TDemoLoggerForm.FormDestroy(Sender: TObject);
|
||||
begin
|
||||
FreeDefLogger;
|
||||
end;
|
||||
|
||||
end.
|
@ -11,6 +11,7 @@ markdown-редакторы не подошли, т.к. они либо не у
|
||||
|
||||
Возможные темы для дальнейшей работы над статьёй:
|
||||
|
||||
- необходимо проверить точность GetTickCount на современной компьютере с Windows 10 20H2
|
||||
- обработка исключений в потоках
|
||||
- логгирование событий, происходящих в потоках
|
||||
- передача данных в доп. поток: использование очереди Windows
|
||||
@ -129,7 +130,7 @@ markdown-редакторы не подошли, т.к. они либо не у
|
||||
|
||||
В некоторых случаях Вы можете решить не использовать дополнительные потоки и тем самым упростить свою программу. К сожалению, в таких случаях при запуске длительной операции из главного потока будет происходить подвисание программы, что может вызвать разочарование со стороны пользователей, а также сделает невозможным выполнение нескольких операций одновременно.
|
||||
|
||||
1. Если ваша программа при нажатии кнопки на форме выполняет обращение к базе данных (либо http-запрос, либо другую операцию, которая может длиться несколько секунд), то Вы можете перед выполнением операции выполнить код `Screen.Cursor := crSQLWait`, а после выполнения операции выполнить код `Screen.Cursor := crDefault`. При нажатии кнопки пользователь увидит, что курсор изменил свою форму (песочные часы с надписью SQL), и скорее всего не будет выполнить никаких действий, пока курсор не примет свой обычный вид. Программисты прибегают к этому приёму очень часто. Пример данного подхода находится в папке "ExNotUseThreads".
|
||||
1. Если ваша программа при нажатии кнопки на форме выполняет обращение к базе данных (либо http-запрос, либо другую операцию, которая может длиться несколько секунд), то Вы можете перед выполнением операции выполнить код `Screen.Cursor := crSQLWait`, а после выполнения операции выполнить код `Screen.Cursor := crDefault`. При нажатии кнопки пользователь увидит, что курсор изменил свою форму (песочные часы с надписью SQL), и скорее всего не будет выполнять никаких действий, пока курсор не примет свой обычный вид. Программисты прибегают к этому приёму очень часто. Пример данного подхода находится в папке "ExNotUseThreads".
|
||||
|
||||
Но вы должны помнить, что если операция выполняется более 5 секунд и пользователь щелкнет мышкой на форме, то в заголовке формы появится надпись "не отвечает", в следствие чего могут быть глюки при работе программы, связанные с тем, что активная форма может улететь на задний план, либо может активироваться форма, которая находится позади модальной формы (примечание: в Windows 10 20H2 надпись "не отвечает" почему-то не появляется, возможно Microsoft отказалась от такого подхода и избавила пользователей от проблем с формами).
|
||||
|
||||
@ -2048,7 +2049,7 @@ end;
|
||||
|
||||
:information_source: **Частота срабатывания системного таймера сильно влияет на работу функции `Sleep`!** Если таймер срабатывает очень часто (1000 раз в секунду), то функция Sleep работает с максимальной точностью (плюс/минус 0.5 мс). Если таймер срабатывает раз в 16 мс, то и точность работы функции Sleep составит примерно 16 мс.
|
||||
|
||||
:information_source: Разрешение системного таймера **никак не влияет** на функции `GetTickCount` и `GetTickCount64`. Их точность соответствует максимальному интервалу системного таймера, т.е. примерно 16 мс. В связи с этим я не советую использовать эти функции для измерения интервалов времени (хотя вроде для этого они и были предназначены). Если Вас устраивает точность замеров 1 мс, то лучше используйте обычную функцию `Now`. Если требуется производить замеры в большей точностью, то используйте функцию `QueryPerformanceCounter`, либо более удобный модуль "TimeIntervals.pas", который ранее уже был рассмотрен.
|
||||
:information_source: Разрешение системного таймера **никак не влияет** на функции `GetTickCount` и `GetTickCount64`. Их точность соответствует максимальному интервалу системного таймера, т.е. примерно 16 мс. В связи с этим я не советую использовать эти функции для измерения интервалов времени (хотя вроде для этого они и были предназначены). Если Вас устраивает точность замеров 1 мс, то лучше используйте обычную функцию `Now` (она возвращаёт текущее время с точностью до миллисекунды в том случае, если разрешение системного таймера выставлено в 1 мс). Если требуется производить замеры в большей точностью, то используйте функцию `QueryPerformanceCounter`, либо более удобный модуль "TimeIntervals.pas", который ранее уже был рассмотрен.
|
||||
|
||||
Отметим также следующие особенности планирования потоков в Windows:
|
||||
1. Для каждого ядра процессора планировщик создаёт свою очередь потоков, готовых к запуску. При обращениях к планировщику, не связанных с работой системного таймера (например, при вызове функций ядра `Sleep`, `SwitchToThread`, `SetEvent`), анализ всех потоков не выполняется. Учитывается только информация, заранее подготовленная планировщиком, поэтому такой код диспетчера ядра выполняется максимально быстро.
|
||||
@ -2104,9 +2105,9 @@ end;
|
||||
|
||||
Уровень приоритета потока влияет на выделение процессорного времени как между потоками в рамках одного процесса, так и между потоками различных процессов.
|
||||
|
||||
Изменять уровень приоритета потока не имеет смысла, если выполняется задача, в которой основное время уходит на ожидание какого-либо события.
|
||||
Изменять уровень приоритета потока не имеет смысла, если выполняется задача, в которой основное время уходит на ожидание какого-либо события. Но если Вы производите обработку какой-либо информации и эта обработка значительно нагружает процессор, то Вы можете выставить уровень приоритета в `tpLower`. В этом случае обработка информации будет выполняться только в том случае, если у процессора имеется ядро, которое ничем не занято. Но как только это ядро потребуется потоку с более высоким приоритетом, работа Вашего низкоприоритетного потока будет приостановлена. Периодически Windows отдаёт кванты времени низкоприоритетным потокам, несмотря на то, что выполняется поток с более высоким приоритетом. Для этого Windows на короткое время (в рамках одного кванта) повышает приоритет низкоприоритетного потока.
|
||||
|
||||
Если в Вашей программе работают 2 потока, у одного уровень приоритета `tpNormal`, а у второго `tpLower` и оба потока привязаны к одному и тому же ядру процессора, то первому потоку гораздо чаще будет предоставляться процессорное время (например, 98%). С другой стороны, если у этих же потоков не будет привязки к одному и тому же ядру, то они получат одинаковое процессорное время. Скорее всего, оба варианта – это не то, чего Вы хотите добиться при изменении уровня приоритета потока.
|
||||
Если в Вашей программе работают 2 потока (оба выполняют математические вычисления), у одного уровень приоритета `tpNormal`, а у второго `tpLower` и оба потока привязаны к одному и тому же ядру процессора, то первому потоку гораздо чаще будет предоставляться процессорное время (например, 98%). С другой стороны, если у этих же потоков не будет привязки к одному и тому же ядру, то они получат одинаковое процессорное время на разных ядрах.
|
||||
|
||||
Существует 2 крайних уровня приоритетов: `tpIdle` и `tpTimeCritical`. Поток с приоритетом `tpIdle` получит процессорное время только в том случае, если нет других активных потоков с более высоким приоритетом. Поток с приоритетом `tpTimeCritical` будет забирать себе всё процессорное время, т.е. потоки с более низким приоритетом не получат квант времени, если выполняется поток с приоритетом `tpTimeCritical`. Это верно для одноядерного процессора. Если процессор многоядерный (сейчас это обычная ситуация), то, скорее всего, будут выполняться одновременно и поток с приоритетом `tpIdle` и поток с приоритетом `tpTimeCritical`.
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user