What's wrong with using TThread.Resume? [duplicate]
Asked Answered
D

1

15

Long ago, when I started working with threads in Delphi, I was making threads start themselves by calling TThread.Resume at the end of their constructor, and still do, like so:

constructor TMyThread.Create(const ASomeParam: String);
begin
  inherited Create(True);
  try
    FSomeParam:= ASomeParam;
    //Initialize some stuff here...
  finally
    Resume;
  end;
end;

Since then, Resume has been deprecated in favor to use Start instead. However, Start can only be called from outside the thread, and cannot be called from within the constructor.

I have continued to design my threads using Resume as shown above, although I know it's been deprecated - only because I do not want to have to call Start from outside the thread. I find it a bit messy to have to call:

FMyThread := TMyThread.Create(SomeParamValue);
FMyThread.Start;

Question: What's the reason why this change was made? I mean, what is so wrong about using Resume that they want us to use Start instead?

EDIT After Sedat's answer, I guess this really depends on when, within the constructor, does the thread actually begin executing.

Doublecheck answered 14/3, 2016 at 23:44 Comment(8)
The finally is wrong. What if an exception is raised?Bartle
@David I typically do a lot more here, this is just a primitive example demonstrating the scope of the question. Simply assigning variables surely wouldn't cause an exception, what I already have is already more than is needed.Doublecheck
Doesn't matter what you do, that finally is always wrong. Don't execute thread on partially constructed object whose destructor is about to run.Bartle
Regard what to do: 1. Remove the try/finally, 2. Remove the call to Resume, 3. Pass False to the inherited constructor. 4. Let TThread.AfterConstruction start the thread when all constructors, even derived constructors, have completed.Bartle
It is always a wise decision to look at the source how it is imlemented. The current implementation also respects the different behaviour of Windows 2003 Server starting the thread within the call of CreateThreadAcrobatics
@SirRufo I think you might be reading the documentation incorrectly. In all versions of window, if CREATE_SUSPENDED is not passed, then the thread may begin executing before CreateThread returns. The Windows Server 2003 specific behaviour is described in the previous paragraph in the docs and relates to tokens and impersonation.Bartle
What makes this interesting is that the overloaded constructor Create(CreateSuspended: Boolean); is NOT deprecated, while Resume is.Doublecheck
@JerryDodge No. There are still reasons why you might want to create suspended. Then when you wish the thread to start, call Start. Simple as that.Bartle
P
13

The short and pithy answer is because the authors of the TThread class didn't trust developers to read or to understand the documentation. :)

Suspending and resuming a thread is a legitimate operation for only a very limited number of use cases. In fact, that limited number is essentially "one": Debuggers

Undesirables

The reason it is considered undesirable (to say the least) is that problems can arise if a thread is suspended while (for example) it owns a lock on some other synchronization object such as a mutex or sempahore etc.

These synchronization objects are specifically designed to ensure the safe operation of a thread with respect to other threads accessing shared resources, so interrupting and interfering with these mechanisms is likely to lead to problems.

A debugger needs a facility to directly suspend a thread irrespective of these mechanisms for surprisingly similar reasons.

Consider for example that a breakpoint involves an implicit (or you might even say explicit) "suspend" operation on a thread. If a debugger halts a thread when it reaches a break-point then it must also suspend all other threads in the process precisely because they will otherwise race ahead doing work that could interfere with many of the low level tasks that the debugger might be asked to then do.

The Strong Arm of the Debugger

A debugger cannot "inject" nice, polite synchronization objects and mechanisms to request that these other thread suspend themselves in a co-ordinated fashion with some other thread that has been unceremoniously halted (by a breakpoint). The debugger has no choice but to strong-arm the threads and this is precisely what the Suspend/Resume API's are for.

They are for situations where you need to stop a thread "Right now. Whatever you are doing I don't care, just stop!". And later, to then say "OK, you can carry on now with whatever it was you were doing before, whatever it was.".

Well Behaved Threads Behave Well Toward Each Other

It should be patently obvious that this is not how a well-behaved thread interacts with other threads in normal operation (if it wishes to maintain a state of "normal" operation and not create all sorts of problems). In those normal cases a thread very much does and should care what those other threads are doing and ensure that it doesn't interfere, using appropriate synchronization techniques to co-ordinate with those other threads.

In those cases, the legitimate use case for Resuming a thread is similarly reduced to just one, single mode. Which is, that you have created and initialised a thread that you do not wish to run immediately but to start execution at some later point in time under the control of some other thread.

But once that new thread has been started, subsequent synchronization with other threads must be achieved using those proper synchronization techniques, not the brute force of suspending it.

Start vs Suspend/Resume

Hence it was decided that Suspend/Resume had no real place on a general purpose thread class (people implementing debuggers could still call the Windows API's directly) and instead a more appropriate "Start" mechanism was provided.

Hopefully it should be apparent that even though this Start mechanism employs the exact same API that the deprecated Resume method previously employed, the purpose is quite different.

Ptomaine answered 15/3, 2016 at 4:6 Comment(8)
I would say that designers of languages and frameworks learn not to trust users with GOOD REASON. The kind of mediocre developer who takes the pieces handed to him and does whatever it PERMITS, without understanding the underlying things appears to outnumber thoughtful software ENGINEERS who try to learn the whole system all the way down, about 10:1 in my experience. Thus, no amount of warnings are enough. It would have been nice if there was a TWorkerThread but since there wasn't, developers "made do" and made a mess. Then they blame the RTL.Acetylcholine
Even if there were a TWorkerThread there would have been ways and means of accessing its handle to be passed to the OS Suspend/Resume APIs. Where do you stop ? Should the Windows API not have provided the Suspend/Resume API's themselves ? How then would anyone implement a debugger sensibly ? The same distrust reasoning applies to any number of facilities and capabilities that are provided by API's and even languages.Ptomaine
TThread isn't intended for use with debuggers. The Win32 APIs are fine for that.Bartle
If a developer can't be expected to use TThread with Suspend/Resume capabilities correctly then how can they possibly be expected to use the Windows Suspend/Resume API's correctly? It seems to me that they simply shouldn't be allowed near a compiler of any sort without adult supervision. :) A bad worker blaming their tools does not mean that the tools need "fixing". The bad worker will still do bad work and continue to blame their tools.Ptomaine
I don't think the deprecation was to save incompetent programmers from themselves. I think the deprecation was for x-plat. Suspend/resume is Windows only no?Bartle
I was addressing your reasoning/the reasoning of those who say that Suspend/Resume are intrinsically "bad". w.r.t the timeline, as of Delphi 7 TThread had x-plat Suspend/Resume, for Windows and for (almost) POSIX thread support on Linux for Kylix. However, it is noted in the implementation that if Linux handled POSIX threads properly then the work-around wouldn't work. But in essence, x-plat considerations came first. Deprecation happened only later. Perhaps they realised that they couldn't achieve x-plat reliably so plumped for lowest common denominator instead. :shrug:Ptomaine
You are putting words into my mouth now. I did not ever say that here. Although since SuspendThread and ResumeThread are for debuggers only, and since TThread is for defining threads and not debugging threads defined by others, Suspend and Resume have always been pointless. No doubt the original TThread designer made the same mistake as so many others did and inferred that SuspendThread was safe for non-debuggers.Bartle
"Pointless" ? Unless perhaps you were implementing a scripting or other dynamic execution environment which might wish to create and debug threads ? No words were intended to be placed in your mouth - you appeared to be supporting/following up Warren's "good reasons not to trust developers". Apologies if that was not your intention. You're probably right that it was initially included for the wrong reason, but that doesn't make removing it for the wrong reason any more right. :)Ptomaine

© 2022 - 2024 — McMap. All rights reserved.