1
0
mirror of https://github.com/loginov-dmitry/multithread.git synced 2025-12-26 15:46:46 +02:00
Files
multithread/CalcTimeQuant/CalcTimeQuantUnit.pas

231 lines
7.7 KiB
ObjectPascal

unit CalcTimeQuantUnit;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls, CheckLst, MMSystem;
type
TCalcQuantThread = class(TThread)
private
// Добавляет длительность интервала активности (квант времени)
procedure AddToWorkList(WorkTime: Double);
// Добавляет длительность интервала бездействия (в спящем состоянии)
procedure AddToNotWorkList(NotWorkTime: Double);
protected
procedure Execute; override;
public
ThreadNum: Integer; // Номер потока
IsFinish: Boolean; // Флаг "работа потока завершена"
WorkAll: Double; // Общее время работы
NotWorkAll: Double; // Общее время бездействия
LoopCount: Integer; // Количество циклов
WorkList: array of Double; // Длительность выделенных квантов времени
NotWorkList: array of Double; // Длительность интервалов простоя
constructor Create(ThreadNum: Integer; AffinityMask: DWORD; APriority: TThreadPriority);
end;
TForm1 = class(TForm)
Label1: TLabel;
Label2: TLabel;
edThreadCount: TEdit;
btnStartThreads: TButton;
Label3: TLabel;
Memo1: TMemo;
Timer1: TTimer;
Label4: TLabel;
clbCPUList: TCheckListBox;
cbUseDiffPriority: TCheckBox;
Label5: TLabel;
cbPriority: TComboBox;
Label6: TLabel;
edSysTimerInterval: TEdit;
btnChangeSysTimerInterval: TButton;
procedure btnStartThreadsClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure Timer1Timer(Sender: TObject);
procedure btnChangeSysTimerIntervalClick(Sender: TObject);
private
{ Private declarations }
FList: TList; // Список запущенных потоков
function GetAffinityMask: DWORD;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.btnChangeSysTimerIntervalClick(Sender: TObject);
var
NewInterval, Res: Cardinal;
begin
{Внимание! Перед изменением разрешения системного таймера проверьте его текущее
разрешение с помощью утилиты Clockres.exe, её можно скачать с сайта Microsoft.
Также желательно закрыть Delphi, т.к. она может принудительно выставлять
разрешение в 1 мс. Браузеры также могут менять разрешение!}
{
This function affects a global Windows setting. Windows uses the lowest value
(that is, highest resolution) requested by any process. Setting a higher
resolution can improve the accuracy of time-out intervals in wait functions.
However, it can also reduce overall system performance, because the thread
scheduler switches tasks more often.
}
NewInterval := StrToInt(edSysTimerInterval.Text);
Res := timeBeginPeriod(NewInterval);
if Res = TIMERR_NOCANDO then
raise Exception.Create('Задано недопустимое разрешение таймера!');
end;
procedure TForm1.btnStartThreadsClick(Sender: TObject);
var
I: Integer;
APriority: TThreadPriority;
begin
Memo1.Clear;
APriority := TThreadPriority(cbPriority.ItemIndex);
for I := 1 to StrToInt(edThreadCount.Text) do
begin
if cbUseDiffPriority.Checked and (I > 1) then
begin
if APriority > tpIdle then
Dec(APriority);
end;
FList.Add(TCalcQuantThread.Create(I, GetAffinityMask, APriority));
end;
btnStartThreads.Enabled := False;
end;
procedure TForm1.FormCreate(Sender: TObject);
var
Info: SYSTEM_INFO;
I: Integer;
begin
FList := TList.Create;
DecimalSeparator := '.';
GetSystemInfo(Info);
for I := 1 to Info.dwNumberOfProcessors do
clbCPUList.Items.Add('cpu #' + IntToStr(I));
clbCPUList.Checked[0] := True;
end;
procedure TForm1.FormDestroy(Sender: TObject);
var
I: Integer;
begin
for I := 0 to FList.Count - 1 do
TCalcQuantThread(FList[I]).Free;
FList.Free;
end;
function TForm1.GetAffinityMask: DWORD;
var
I: Integer;
begin
Result := 0;
for I := 0 to clbCPUList.Count - 1 do
if clbCPUList.Checked[I] then
Result := Result or (1 shl I);
end;
procedure TForm1.Timer1Timer(Sender: TObject);
var
I: Integer;
Q: Double;
T: TCalcQuantThread;
s: string;
begin
for I := FList.Count - 1 downto 0 do
begin
T := TCalcQuantThread(FList[I]);
if T.IsFinish then
begin
s := Format('Интервалы активности потока #%d (Общее время=%f; Число квантов=%d; '+
'Число циклов=%d): ', [T.ThreadNum, T.WorkAll, Length(T.WorkList), T.LoopCount]);
for Q in T.WorkList do
s := s + FormatFloat('0.0000', Q) + ',';
Memo1.Lines.Add(s);
s := Format('Интервалы бездействия потока #%d (Общее время=%f; Число интервалов '+
'бездействия=%d): ', [T.ThreadNum, T.NotWorkAll, Length(T.NotWorkList)]);
for Q in T.NotWorkList do
s := s + FormatFloat('0.0000', Q) + ',';
Memo1.Lines.Add(s + sLineBreak);
T.Free;
FList.Delete(I);
end;
end;
if FList.Count = 0 then
btnStartThreads.Enabled := True;
end;
{ TCalcQuantThread }
procedure TCalcQuantThread.AddToNotWorkList(NotWorkTime: Double);
begin
SetLength(NotWorkList, Length(NotWorkList) + 1);
NotWorkList[High(NotWorkList)] := NotWorkTime;
NotWorkAll := NotWorkAll + NotWorkTime;
end;
procedure TCalcQuantThread.AddToWorkList(WorkTime: Double);
begin
SetLength(WorkList, Length(WorkList) + 1);
WorkList[High(WorkList)] := WorkTime;
WorkAll := WorkAll + WorkTime;
end;
constructor TCalcQuantThread.Create(ThreadNum: Integer; AffinityMask: DWORD;
APriority: TThreadPriority);
begin
inherited Create(False);
Self.ThreadNum := ThreadNum;
if AffinityMask > 0 then
SetThreadAffinityMask(Self.Handle, AffinityMask);
Priority := APriority;
end;
procedure TCalcQuantThread.Execute;
var
StartTicks, BreakDiff, QuantDiff, CurQuantTime: Int64;
PrevTicks, CurTicks, CurQuantStart: Int64;
Freq: Int64;
begin
QueryPerformanceFrequency(Freq);
QueryPerformanceCounter(StartTicks);
PrevTicks := StartTicks;
CurQuantStart := StartTicks;
BreakDiff := 10 * Freq;
QuantDiff := Round(0.001 * Freq);
CurQuantTime := 0;
repeat
QueryPerformanceCounter(CurTicks);
Inc(LoopCount);
if CurTicks - PrevTicks > QuantDiff then
begin // Если разница оказалась больше 1 мс, значит ОС приостанавливала
// работу потока и теперь начался отсчёт нового кванта
AddToWorkList(CurQuantTime / Freq); // Сохраняем время работы потока
AddToNotWorkList((CurTicks - PrevTicks) / Freq); // Сохраняем время простоя потока
CurQuantStart := CurTicks;
CurQuantTime := 0;
end else
CurQuantTime := CurTicks - CurQuantStart;
PrevTicks := CurTicks;
until (CurTicks - StartTicks) > BreakDiff;
if CurQuantTime > 0 then // Обрабатываем длительность последнего кванта
AddToWorkList(CurQuantTime / Freq);
IsFinish := True;
end;
end.