You've already forked multithread
mirror of
https://github.com/loginov-dmitry/multithread.git
synced 2025-12-26 15:46:46 +02:00
231 lines
7.7 KiB
ObjectPascal
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.
|