diff --git a/CalcTimeQuant/CalcTimeQuantUnit.dfm b/CalcTimeQuant/CalcTimeQuantUnit.dfm index 810ced5..cec3e4e 100644 --- a/CalcTimeQuant/CalcTimeQuantUnit.dfm +++ b/CalcTimeQuant/CalcTimeQuantUnit.dfm @@ -5,7 +5,7 @@ object Form1: TForm1 #1055#1088#1086#1075#1088#1072#1084#1084#1072' '#1076#1083#1103' '#1086#1087#1088#1077#1076#1077#1083#1077#1085#1080#1103' '#1076#1083#1080#1090#1077#1083#1100#1085#1086#1089#1090#1080' '#1074#1099#1076#1077#1083#1103#1077#1084#1099#1093' '#1082#1074#1072#1085#1090#1086#1074' '#1074#1088#1077#1084#1077#1085 + #1080 ClientHeight = 434 - ClientWidth = 646 + ClientWidth = 730 Color = clBtnFace Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText @@ -16,7 +16,7 @@ object Form1: TForm1 OnCreate = FormCreate OnDestroy = FormDestroy DesignSize = ( - 646 + 730 434) PixelsPerInch = 96 TextHeight = 19 @@ -58,6 +58,13 @@ object Form1: TForm1 Height = 19 Caption = #1055#1088#1080#1074#1103#1079#1072#1090#1100' '#1082' '#1103#1076#1088#1091' CPU:' end + object Label5: TLabel + Left = 296 + Top = 83 + Width = 206 + Height = 19 + Caption = #1055#1088#1080#1086#1088#1080#1090#1077#1090' '#1087#1077#1088#1074#1086#1075#1086' '#1087#1086#1090#1086#1082#1072':' + end object edThreadCount: TEdit Left = 192 Top = 52 @@ -68,7 +75,7 @@ object Form1: TForm1 end object btnStartThreads: TButton Left = 304 - Top = 103 + Top = 110 Width = 297 Height = 41 Caption = #1047#1072#1087#1091#1089#1090#1080#1090#1100' '#1087#1086#1090#1086#1082#1080' '#1085#1072' 10 '#1089#1077#1082#1091#1085#1076 @@ -78,11 +85,12 @@ object Form1: TForm1 object Memo1: TMemo Left = 8 Top = 167 - Width = 630 + Width = 714 Height = 259 Anchors = [akLeft, akTop, akRight, akBottom] ScrollBars = ssVertical TabOrder = 2 + ExplicitWidth = 630 end object clbCPUList: TCheckListBox Left = 192 @@ -100,6 +108,25 @@ object Form1: TForm1 Caption = #1048#1089#1087#1086#1083#1100#1079#1086#1074#1072#1090#1100' '#1088#1072#1079#1085#1099#1077' '#1087#1088#1080#1086#1088#1080#1090#1077#1090#1099 TabOrder = 4 end + object cbPriority: TComboBox + Left = 506 + Top = 80 + Width = 216 + Height = 27 + Style = csDropDownList + ItemHeight = 19 + ItemIndex = 3 + TabOrder = 5 + Text = 'tpNormal ' + Items.Strings = ( + 'tpIdle ' + 'tpLowest ' + 'tpLower ' + 'tpNormal ' + 'tpHigher ' + 'tpHighest' + 'tpTimeCritical') + end object Timer1: TTimer Interval = 500 OnTimer = Timer1Timer diff --git a/CalcTimeQuant/CalcTimeQuantUnit.pas b/CalcTimeQuant/CalcTimeQuantUnit.pas index 8af738c..f35a4ae 100644 --- a/CalcTimeQuant/CalcTimeQuantUnit.pas +++ b/CalcTimeQuant/CalcTimeQuantUnit.pas @@ -38,6 +38,8 @@ type Label4: TLabel; clbCPUList: TCheckListBox; cbUseDiffPriority: TCheckBox; + Label5: TLabel; + cbPriority: TComboBox; procedure btnStartThreadsClick(Sender: TObject); procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); @@ -63,8 +65,8 @@ var APriority: TThreadPriority; begin Memo1.Clear; - //APriority := tpNormal; - APriority := tpHigher; + + APriority := TThreadPriority(cbPriority.ItemIndex); for I := 1 to StrToInt(edThreadCount.Text) do begin if cbUseDiffPriority.Checked and (I > 1) then diff --git a/multithread_in_delphi_for_beginners.md b/multithread_in_delphi_for_beginners.md index 755ef87..f2eada8 100644 --- a/multithread_in_delphi_for_beginners.md +++ b/multithread_in_delphi_for_beginners.md @@ -128,13 +128,13 @@ Delphi – это прежде всего инструмент для разра # 2. Базовый класс многопоточности - TThread -TThread – это базовый класс, инкапсулирующий функционал для обеспечения работы параллельного потока. Если мы хотим реализовать код, который должен выполняться в отдельном потоке, то нам необходимо реализовать наследника от класса TThread и, как минимум, реализовать override-метод Execute. Код, находящийся внутри метода Execute будет исполняться в отдельном потоке. +`TThread` – это базовый класс, инкапсулирующий функционал для обеспечения работы параллельного потока. Если мы хотим реализовать код, который должен выполняться в отдельном потоке, то нам необходимо реализовать наследника от класса `TThread` и, как минимум, реализовать override-метод Execute, который перекроет виртуальный абстрактный метод `Execute`, объявленный в родительском классе `TThread`. Код, находящийся внутри метода `Execute` будет исполняться в отдельном потоке. -При создании класса-наследника от TThread мы можем реализовать конструктор, деструктор, объявить собственные поля, методы, свойства и т.д., в зависимости от поставленной перед нами задачи. +При создании класса-наследника от `TThread` мы можем реализовать конструктор, деструктор, объявить собственные поля, методы, свойства и т.д., в зависимости от поставленной перед нами задачи. В данном разделе я не буду подробно останавливаться на описании возможностей класса TThread. Читайте внимательно следующие разделы, надеюсь, в них Вы найдёте необходимую для Вас информацию. -:information_source: **Информация!** *Дополнительные потоки создаются с помощью вызова соответствующей функции операционной системы, например CreateThread в ОС Windows. Класс TThread является обёрткой, которая добавляет к системному дополнительному потоку объектно-ориентированное измерение, т.е. позволяет создать объект, который привязывается к системному дополнительному потоку.* +:information_source: **Информация!** *Дополнительные потоки создаются с помощью вызова соответствующей функции операционной системы, например `CreateThread` в ОС Windows. Класс `TThread` является обёрткой, которая добавляет к системному дополнительному потоку объектно-ориентированное измерение, т.е. позволяет создать объект, который привязывается к системному дополнительному потоку.* :information_source: **Информация!** Существуют и другие популярные средства для многопоточного программирования в Delphi. К ним стоит отнести: \- **Parallel programming library** - стандартная библиотека для параллельного программирования в современных версиях Delphi. @@ -972,6 +972,8 @@ end. 8) Кнопка `btnRunInParallelThread` может быть нажата произвольное количество раз. +:warning: **Внимание!** *Функции `InterlockedIncrement` и `InterlockedDecrement` являются частью Windows API. В современных версиях Delphi Вы можете использовать их кроссплатформенные аналоги: `AtomicIncrement` и `AtomicDecrement`*. + # 5. Передача информации из дополнительного потока в основной При разработке многопоточных приложений в Delphi очень актуальной является задача передачи информации, подготовленной в дополнительном потоке, в главный поток. Это весьма сложная тема, требующая от Delphi-программиста повышенного внимания, т.к. при недостаточных знаниях у программиста есть очень высокий шанс сделать программу глюченной и нестабильной. @@ -1972,14 +1974,17 @@ end; tpTimeCritical) platform; {$ENDIF MSWINDOWS} ``` -Обычно программисту нет смысла изменять приоритеты потоков. Приоритет потока никак не влияет на скорость исполнения программного кода. Приоритет потока никак не влияет на размер кванта времени. Относительный приоритет потока работает только в рамках процесса и никак не влияет на выделение квантов времени потокам, которые работают в других процессах. +Обычно программисту нет смысла изменять приоритеты потоков. Приоритет потока никак не влияет на скорость исполнения программного кода. Приоритет потока никак не влияет на размер кванта времени (однако размер кванта времени зависит от того, является ли приложение активным или нет, т.е. у неактивного приложения квант времени примерно в 3 раза меньше, чем у активного). -Изменять приоритет потока не имеет никакого смысла, если выполняется задача, в которой основное время уходит на ожидание какого-либо события. +Относительный приоритет потока влияет на выделение процессорного времени как между потоками в рамках одного процесса, так и между потоками различных процессов. + +Изменять приоритет потока не имеет смысла, если выполняется задача, в которой основное время уходит на ожидание какого-либо события. Если в Вашей программе работают 2 потока, у одного приоритет `tpNormal`, а у второго `tpLower` и оба потока привязаны к одному и тому же ядру процессора, то первому потоку гораздо чаще будет предоставляться процессорное время (например, 98%). С другой стороны, если у этих же потоков не будет привязки к одному и тому же ядру, то они получат одинаковое процессорное время. Скорее всего, оба варианта – это не то, чего Вы хотите добиться при изменении относительного приоритета потока. Существует 2 крайних уровня приоритетов: `tpIdle` и `tpTimeCritical`. Поток с приоритетом `tpIdle` получит процессорное время только в том случае, если нет других активных потоков с более высоким приоритетом. Поток с приоритетом `tpTimeCritical` будет забирать себе всё процессорное время, т.е. потоки с более низким приоритетом не получат квант времени, если выполняется поток с приоритетом `tpTimeCritical`. Это верно для одноядерного процессора. Если процессор многоядерный (сейчас это обычная ситуация), то, скорее всего, будут выполняться одновременно и поток с приоритетом `tpIdle` и поток с приоритетом `tpTimeCritical`. +:information_source: **Внимание!** *В каталоге CalcTimeQuant находится программа, позволяющая производить эксперименты с приоритетами и замеры длительности квантов времени.*