1. wallapopmanager13.03.2025 в 22:13от
Загрузка...

[delphi] Реализация многопоточности

Тема в разделе "С/С++/Pascal/Delphi", создана пользователем ArhiDem0n, 24.09.2013.

  1. ArhiDem0n

    ArhiDem0n

    Статус:
    Оффлайн
    Регистрация:
    24.09.13
    Сообщения:
    5
    Репутация:
    0 +/-
    Реализация многопоточности в delphi.
    Вижу множество вопросов на разных форумах по программированию которые относятся к многопоточности. В этой статье я бы хотел не только объяснить как реализовать это в delphi, но и предоставить готовый шаблон многопоточного приложения который вы бы могли использовать в дальнейшем в своих программах.

    Для начала немного теории. Одной из важнейших вещей в многопоточном приложении является синхронизация потоков. Если не учесть, синхронизацию потоков, то это, может привести к плачевным последствиям. Давайте рассмотрим такой пример, после чего у вас в голове всё встанет на свои места.
    У нас в программе имеется переменная, назовём её - "int" - типа integer. Если мы будем присваивать ей значение из основного потока, то всё будет хорошо, и проблем не возникнет. Мало того, если даже мы присвоим ей значение из дополнительного потока который мы создали, то всё будет хорошо. Но так как зачастую у многопоточного приложения более одного дополнительного потока, то зададимся вопросом- "А, что будет если мы попытаемся присвоить переменной значение из двух потоков одновременно?". Вот тут, и произойдёт то таинственное события при котором переменной присвоится значение которое скорее всего не будет равно ни одному из значений которых вы планировали ей присвоить. Тогда и вступает в игру метод синхронизации synchronize. С помощь данного нам разработчиками delphi метода, вы можете обеспечить безопасное обращение нескольких потоков к одной переменной. Данный метод обеспечивает следующие: при вызове процедуры с помощью synchronize она начинает выполнятся, а все остальные процедуры вызванные с помощью этого метода встают в очередь и ждут выполнения текущей процедуры. Всё вышеописанное относится только к записи переменных, а читать можно одновременно из нескольких потоков и ничего плохого не произойдёт. Ну а теперь перейдём к практике.

    И так создадим новый проект. И начнём писать поток. После вот этих строк:
    Код:
    private
        { Private declarations }
      public
        { Public declarations }
      end;
    
    Добавим свои:
    Код:
    potok = class(TThread) //Этой строкой мы унаследовали класс потока
      private
       str: string;//в разделе private описываются переменные с помощью которых мы 
       nomer : Integer;//будем передавать значения между процедурами внутри потока 
      protected
        procedure Execute; override;//это главная процедура потока, она начинает свою работу 
    //после того как мы создали поток
      public
        procedure synchro;//в разделе public вы можете объявить процедуры какие только душе 
    //угодно
        constructor Create(CreateSuspended: Boolean);//эта строка говорит о том, что мы в
    //implementation опишем конструкцию 
    //потока
      end;
    
    Ну здесь вроде бы всё понятно, вы можете добавлять сколько угодно переменных и процедур для удобства работы.

    Под implementation нам надо описать конструкцию потока, в данном примере это не обязательно, но, что бы вы знали на будущие мы всё равно это сделаем.
    Код:
    constructor potok.Create(CreateSuspended: Boolean);
    begin
      inherited Create(CreateSuspended);//Эта строка говорит о том, что поток после создания 
    //будет приостановлен если ему передать значение true при создание, если false, то сразу 
    //начнёт работу.
    end;
    
    Добавим в глобальные переменные переменную - "nom" - типа integer, а так же массив наших потоков.
    Код:
    var
      Form1: TForm1;
      nom:integer;
      a: array [1..10] of potok;//массив для хранения наших потоков
    
    Теперь нужно описать процедуры потока, под implementation добавляем следующие:
    Код:
    procedure potok.Execute;//начинаем описывать главную процедуру потока
    var
    i:integer;
    begin
    for i:=0 to 100 do
    begin
    sleep(1000);
    synchronize(synchro);//этой строкой мы вызываем процедуру synchro в единичном экземпляре
    end;
    end;
    
    procedure potok.synchro; //описываем ещё одну процедуру потока, которая будет менять 
    //загаловок form1
    begin
    inc(nom);
    form1.Caption:='Поток делает своё дело - '+inttostr(nom);
    end;
    
    Собственно всё готово, остаются только запустить потоки. Кидаем кнопку на форму, "кастуем на неё даблклик" и пишем:
    Код:
    procedure TForm1.Button1Click(Sender: TObject);
    var
    pot:integer;
    begin
    for pot:=1  to 10 do  //цикл запускает 10 потоков, которые будут изменять заголовок
      a[pot]:=potok.Create(false); //формы, так же идёт 
    //добавление в массив, что бы потом вы могли уничтожить 
    //один поток.
      end;
    
    Вот так выглядит программа в итоге:
    Код:
    unit Unit1;
    
    interface
    
    uses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, StdCtrls;
    
    type
      TForm1 = class(TForm)
        Button1: TButton;
    
        procedure Button1Click(Sender: TObject);
      private
        { Private declarations }
      public
        { Public declarations }
      end;
    potok = class(TThread) //Этой строкой мы унаследовали класс потока
      private
       str: string;//в разделе private описываются переменные с помощью которых мы
       nomer : Integer;//будем передавать значения между процедурами внутри потока
      protected
        procedure Execute; override;//это главная процедура потока, она начинает свою работу
    //после того как мы создали поток
      public
        procedure synchro;//в разделе public вы можете объявить процедуры какие только душе
    //угодно
        constructor Create(CreateSuspended: Boolean);//эта строка говорит о том, что мы в
    //implementation опишем конструкцию
    //потока
      end;
    
    var
    a: array [1..10] of potok;
      Form1: TForm1;
       nom:integer;
    implementation
    constructor potok.Create(CreateSuspended: Boolean);
    begin
      inherited Create(CreateSuspended);//Эта строка говорит о том, что поток после создания
    //будет приостановлен если ему передать значение true при создание, если false, то сразу
    //начнёт работу.
    end;
    {$R *.dfm}
    
    procedure TForm1.Button1Click(Sender: TObject);
    var
    pot:integer;
    begin
    for pot:=1  to 10 do  //цикл запускает 10 потоков, которые будут изменять заголовок
      a[pot]:=potok.Create(false); //формы, так же идёт добавление в массив, что бы потом вы могли их уничтожить по одному.
      end;
    
    procedure potok.Execute;//начинаем описывать главную процедуру потока
    var
    I:integer;
    begin
    for i:=0 to 100 do
    begin
    sleep(1000);
    synchronize(synchro);//этой строкой мы вызываем процедуру synchro в единичном экземпляре
    end;
    end;
    
    procedure potok.synchro; //описываем ещё одну процедуру потока, которая будет менять
    //загаловок form1
    begin
    inc(nom);
    form1.Caption:='Поток делает своё дело - '+inttostr(nom);
    end;
    
    
    end.
    
    Запускаем программу, и нажимаем на кнопку. И видим как в заголовке формы по очереди увеличивается число. ЧТД как говорится. Удачи в создание приложений.