0

The perfect way to run and terminate threads in Windows using C is mentioned in the answer below!

There are 2 problems I'm facing with the current implementation method :

  1. I can't forcibly stop the thread. For some reason it still continues. For example I have a for loop, it runs a function of which this thread example is a part. When this function is called 4-5 times, I see multiple animations on the screen suggesting that the previous threads didn't stop even when I called TerminateThread function at the end of my function.

  2. At times the thread doesn't run at all and no animation is displayed on the screen. Which is if my function code runs really fast or for some other reason, I feel like the thread is being killed before it initializes. Is there a way to wait until init of thread?

How do I fix these issues?

  • 4
    You must not use `TerminateThread`. The documentation for that function, which you should have read, tells you not to do so. Use a variable or event to signal the thread when it is time for it to terminate. As for the other problem, likely you have a race on stdout. Hard to be sure without a [mcve]. – David Heffernan Jun 18 '18 at 09:15
  • You can send a signal to the thread (or set a common flag) to tell it to stop. – Weather Vane Jun 18 '18 at 09:15
  • @David, it is a process memory scanning application. For certain processes like null, system, the code is unable to run. And in these cases the thread is also not run even though I make sure to run the thread first and then try to read process memory. – Total Anime Immersion Jun 18 '18 at 09:19
  • I'm not sure why you directed that comment to me, because it bears no relation to my comment. – David Heffernan Jun 18 '18 at 09:31
  • Very old but still very good reading: [The n Habits of Highly Defective Windows Applications](http://www.flounder.com/badprogram.htm). Mandatory reading for anyone fiddling around with raw Win32 threads. – Lundin Jun 18 '18 at 11:18
  • @Lundin Except that it gets `volatile` wrong. – zett42 Jun 18 '18 at 11:29
  • @zett42 Umm no it doesn't, it gets it absolutely correct, as explained in the link. If you are using some modern PC compiler you might be spoiled with the compiler being aware of variables getting updated from callbacks, but that is not always the case. Notably, it does not suggest to use volatile for the purpose of thread synchronization nor for memory barriers, which is likely what you are thinking of. – Lundin Jun 18 '18 at 11:35
  • 1
    @Lundin: [this paragraph](http://www.flounder.com/badprogram.htm#Using%20PeekMessage%20anywhere) seems to do just that in the "corrected" code: *"note that it does not do any synchronization! Why not? Clearly, the running flag is being accessed by two different threads"*. It uses a `volatile bool` flag for signaling a running thread, without any other synchronization. If this code was compiled with [`-volatile:iso`](https://msdn.microsoft.com/en-us/library/jj204392.aspx), which is supposed to be "standard" way, the compiler wouldn't make ordering and visibility guarantees. – vgru Jun 18 '18 at 11:54
  • 1
    @Lundin The article suggests to use `volatile` as a tool that is required in multithreaded applications. According to [C++ standard](http://en.cppreference.com/w/cpp/language/cv), `volatile` has nothing to do with multithreading at all. There is a [Microsoft extension](https://learn.microsoft.com/en-us/cpp/cpp/volatile-cpp) that changes semantics of `volatile`, but the article doesn't mentioned that so it is wrong. – zett42 Jun 18 '18 at 11:57
  • The perfect way to terminate threads in Windows is to not do it until the process is terminated and the OS terminates them. – Martin James Jun 18 '18 at 13:23
  • @Groo Apparently you don't know what CRITICAL_SECTION means... – Lundin Jun 18 '18 at 13:46
  • 1
    @zett42 No, you still haven't understood why the article tells you do use volatile. The remark of why volatile must be used is not really about multithreading but about incorrect compiler optimizations caused by updating a variable from a callback. Everyone who's worked with old s**t compilers knows of this issue. Thread synchronization is another issue entirely. [Read this](https://stackoverflow.com/a/12710939/584518). – Lundin Jun 18 '18 at 13:50
  • @Lundin: At this point, I am pretty sure you are looking at the wrong code snippet. Open the link and do a page search for "CMyWndClass::threadfunc". There are no critical sections in that code, and the author proudly states that "it does not do any synchronization". – vgru Jun 18 '18 at 14:52
  • @Groo Ok that's not where your link lead me. I'm not sure what it has to do with volatile though. As for volatile and re-ordering, didn't we already have that discussion [here](https://stackoverflow.com/a/50800644/584518)? – Lundin Jun 18 '18 at 15:17

1 Answers1

2

Correct way of terminating threads is to signal the thread and let it finish gracefully, i.e.:

(updated to use interlocked intrinsics instead of a volatile flag, as per @IInspectable's comment below)

HANDLE eventHnd;
HANDLE threadHnd;
LONG isStopRequested = 0;  // 1 = "stop requested"

static DWORD WINAPI thread_func(LPVOID lpParam)
{
    do
    {
        // wait until signalled from a different thread
        WaitForSingleObject(eventHnd, INFINITE);

        // end thread if stop requested
        if (InterlockedCompareExchange(&isStopRequested, 0, 0) == 1)
            return 0;

        // otherwise do some background work
        Sleep(500);

    } while (true);
}

The eventHnd variable is initialized using the CreateEvent function, and the stopRequested variable is just a boolean flag you can set from your main program:

// this creates an auto-reset event, initially set to 'false'
eventHnd = CreateEvent(NULL, false, false, NULL);
InterlockedExchange(&isStopRequested, 0);
threadHnd = CreateThread(NULL, 0, Processing_Thread, NULL, 0, NULL);

So, whenever you want to tell the thread do perform a task, you will simply set the event:

SetEvent(eventHnd);

And when you want to end the thread, you will set the flag to true, signal the event, and then wait for the thread to finish:

// request stop
InterlockedExchange(&isStopRequested, 1);

// signal the thread if it's waiting
SetEvent(eventHnd);

// wait until the thread terminates
WaitForSingleObject(threadHnd, 5000);
vgru
  • 49,838
  • 16
  • 120
  • 201
  • `volatile` doesn't do what you appear to think it does. – IInspectable Jun 18 '18 at 09:44
  • @IInspectable: it's hard to tell, because I don't know what you think I think `volatile` does. – vgru Jun 18 '18 at 10:04
  • 1
    You think it serves as a multithreading synchronization primitive. It doesn't. – IInspectable Jun 18 '18 at 10:10
  • @IInspectable: while it's fairly safe to assume that the code will work correctly on Windows -- [MSVC ensures release semantics for all volatile writes + acquire semantics for all volatile reads by default](https://msdn.microsoft.com/en-us/library/jj204392.aspx) -- this is non-standard Microsoft behavior, so I agree it would be better to be explicit about synchronization. `WaitForSingleObject`, `CreateThread` and `InterlockedExchange` are Windows API functions, so this is not portable anyway, but at least `InterlockedExchange` must create a memory barrier according to its specs. – vgru Jun 18 '18 at 11:24
  • I think this is a rather weird design. This won't allow you to kill working threads unless they are done with their job. I prefer to do something like `... WaitForSingleObject(hWakeup, INFINITE); for(i=0; i – Lundin Jun 18 '18 at 11:31
  • @Lundin: that's a nice approach, but the `doALittleBitOfWork(i);` part still determines how long you'll have to wait before the thread gets to read the signal. So, yes, if the user wants to split the work in chunks, s/he can use a for loop and check the condition inside each iteration, but I believe this depends on the use case. The only choice is whether you will use a different event (`hStop` in your comment), or the same flag `for (...) { kill_req = (InterlockedCompareExchange(&isStopRequested, 0, 0) == 1); }`. In both cases you have to set the wakeup event after creating the stop request. – vgru Jun 18 '18 at 11:43
  • 'Correct way of terminating threads is to signal the thread and let it finish gracefully,' ... and if the thread is executing a lengthy loop in a library? – Martin James Jun 18 '18 at 13:25
  • @MartinJames: there is only one way to let a thread finish gracefully that I am aware of, unless this lengthy library loop takes a pointer to a function for an early loop termination. What did you have in mind? – vgru Jun 18 '18 at 13:30
  • @MartinJames: If the library doesn't provide an asynchronous interface, your only option is to run it in its own process. Unlike threads, it is safe to terminate a process. – IInspectable Jun 18 '18 at 14:26