There is such a method in one old component:

procedure TTangentThread.SetActive(Value: Boolean); begin if Value <> FActive then begin FActive := Value; case FActive of False: begin FThread.Suspend; if (IsRuntime) and (Assigned(FOnSuspend)) then FOnSuspend(self); end; True: begin FThread.Resume; if (IsRuntime) and (Assigned(FOnResume)) then FOnResume(self); end; end; end; end; 

And in general, it works, but the compiler says that Suspend and Resume are outdated and should not be used. How to rewrite it now without using "obsolete" methods?

  • 2
    These methods have become obsolete only in terms of programming styles. So, if you do not rewrite, you will not lose much. And the "correct" rewrite is to replace the Suspend / Resume functions with work with synchronization objects: for example, SyncObjs.TEvent - the worker thread waits until the event works, does the work, and again (in the cycle) waits for the next event trigger ... If Under some conditions, the thread should skip work, well, you can put the working code in the "if". - velial
  • @velial do not want to issue an answer? - kami
  • @kami in order to issue an answer, you probably need to give an example of more correct work through events. It would have been a desire for velial for this, I think he would have written an answer right away) - Isaev
  • @Isaev ok. I'll try it myself. Moreover, the statement about obsolescence solely from the point of view of style is not entirely correct, you can run into trouble when using shared data. Unfortunately, the best article about multithreading (was on Vingrad) rested in Bose. Together with the old Vinhrad ... - kami

2 answers 2

Suspend and Resume are actually executed by the operating system. For Windows, SuspendThread and ResumeThread are called inside these methods .

Functions "very much alive", but ... you really shouldn't use them. The problem is that they work with the flow "here and now." That is, if the Suspend command finds a stream in the middle of an operator (for example, assigning a string), then it is at this point that the stream will fall asleep.

Naturally, such "arbitrariness" does not contribute to the high-quality execution of the application, especially if the additional and main stream work on shared (joint) data. For example, the additional stream began to change the array (string, list, etc.), at this time it was "fallen asleep", the main thread reset the array and woke up the additional one. In the additional stream, the nominal dimension check has already been passed, so he does not know that the element on which the stream is trying to work is no longer there. As a result - guaranteed damage to memory with all the ensuing consequences.

That is why you need to use synchronization objects: TCriticalSection , TMutex , TEvent , TSemaphore , TMultiReadExclusiveWriteSynchronizer , which TMultiReadExclusiveWriteSynchronizer access to shared resources and / or send "correct", at "right times" control signals. An alternative (non-cross-platform) is to use a message mechanism .

As an example: an additional thread must perform some work on a command and wait for a new command:

 uses System.SyncObjs; type TmyThread = class(TThread) private FEvent: TEvent; // "синхронизатор" procedure DoWork; // в этом методе будет выполняться полезная работа protected procedure Execute; override; public constructor Create; destructor Destroy; override; procedure StartSingleWork; // этот метод вызывается "извне" end; { TmyThread } constructor TmyThread.Create; begin inherited Create(False); FEvent := TEvent.Create; // создаем сигнальное событие end; procedure TmyThread.Execute; begin while not Terminated do begin FEvent.WaitFor; // ожидаем, пока взведут событие FEvent.ResetEvent; // сбрасываем событие,чтобы // опять войти в ожидание на WaitFor if not Terminated then // и если поток не уничтожают DoWork; // делаем свое дело. end; end; procedure TmyThread.StartSingleWork; begin // кто-то извне хочет, чтобы поток выполнил свою работу FEvent.SetEvent; // выводим поток из спячки. // поток будет выведен из ожидания в WaitFor, // выполнит полезную работу и опять заснет. end; procedure TmyThread.DoWork; begin // здесь выполняется какая-то полезная работа // в контексте нашего дополнительного потока. end; destructor TmyThread.Destroy; begin Terminate; // начинаем уничтожение FEvent.SetEvent; // выводим поток из спячки while not Finished do // ждем, пока он завершится Sleep(0); FreeAndNil(FEvent); // и уничтожаем содержимое потока. inherited; end; 

In addition: in my opinion, the best of the articles about multithreading in Delphi (and not only). Unfortunately, only in web.archive ...

Upd. Vinhrad's old forum came to life, direct link to the article

    These methods are not "outdated", the matter is different. As far as I know, they are used by the debugger to control threads during debugging. And if these methods are used in your application, you can run into "incomprehensible glitches" during debugging. Or what will happen to your application if you pause a thread while it is busy with some critical system object? In an amicable way, it is impossible to suspend the flow of work, you can "ask" it to stop at a convenient time for it. To do this, you must use synchronization objects, such as TEvent . The thread in its cycle checks the state of this synchronization object and either waits until it can work or it works.

    If you need to start a thread only once at the right time, then you can use the Start method.

    If you really want to get rid of compiler warnings, you can use the Suspended property ( Suspended := False; or Suspended := True; ) instead of Suspend and Resume . This will remove the compiler warnings, but it will not cure potential problems.

    • I wonder why the person who put a minus for the answer was too lazy to write a comment? What is wrong with my answer? - kot-da-vinci
    • Because it was not at all because of the debugger that they were recognized as obsolete. They simply do not allow for synchronization with sufficient reliability. - Pavel Mayorov