Exiting Threads Closes Main Thread?

Hi everyone!

New to the forum and I haveta say that there's a wealth of knowledge on here that has helped me GREATLY so far...

Anyway, I'm very new to C++ and my lack of knowledge might be showing here so please bear with me.

I have a simple program that is doing some work for me monitoring pixel colors and executing commands according to changes. What I have works great, but it needs repeated.

To simplify things, I have my Main thread that creates two additional threads.

Thread A grabs the appropriate screen information and stores it via BitBlt on a loop.

Thread B is grabbing and comparing individual pixel color values stored against expected values and acts accordingly.

This works great, however once the "first job" is completed, I need to restart the threads to execute again (or at least Thread B, Thread A can continue to run).

HOWEVER no matter what method I use to exit Thread B, it closes Thread A as well as my Main thread therefore not allowing me to rerun via loop, goto, or any other method.

What am I missing? I'm open to ideas. Googling only leads to the opposite answer... when the main closes before threads...

I'm using VS C++ to write this on a Windows 10 machine if that makes any difference.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <chrono>
#include <iostream>
#include <thread>
#include <windows.h>

using namespace std;

void Thread_A() {

// gets and stores pixel data in memory

}


void Thread_B() {

// uses stored pixel data in memory to execute commands

}


int main() {

// setup code blah blah

std::thread t_Thread_A();
std::thread t_Thread_B();

while (!exit_flag) {   //  !exit_flag only for unexpected need to close

Sleep( for time );

}


Last edited on
Is there a reason you expect Thread A *NOT* to exit?

You have an exit flag, but I don't know where you set it, declare it, or change it later. You should post your whole code.
Well I was hoping not to show how inexperienced I am at code writing and how sloppy it is, but here's the quickly trimmed down current "version" of what I have...

Some things might be in there from previous attempts to use SuspendThread and some other methods I tried. Originally I was going to use the exit_flag to end the threads and just use a loop or goto in order to recreate them for the next problem. Then I tried internally looping the threads and suspending them so I didn't have to exit them. That didn't work either. I couldn't get them to suspend. Finally I tried including a "pause" variable that would be reset in the main thread, but that didn't really work either.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
#include <chrono>
#include <iostream>
#include <thread>
#include <windows.h>

using namespace std;



void PS_activate(bool& exit_flag, bool& event_over) {

// DECLARE VARIABLES

		while ((!exit_flag) && (!event_over)) {
			// WORK
			if ( THIS == TRUE) {
				trigger2 = true;
			}
			if (THAT == TRUE) {
				trigger1 = true;
			}
			if (THING == TRUE) {
				event_over = true;
			}
			std::this_thread::sleep_for(std::chrono::milliseconds(25));
		}
}

void F_Cmds(bool& exit_flag, bool& event_over) {
	
	// INITIALIZING VARIABLES
	bool trigger2 = false;
	bool trigger1 = false;
	
	while (!exit_flag) {
		i = 0;
		pressed = 1;
		std::thread t_PS_Flag(ref(exit_flag), ref(event_over));

		// DOING WORK


		while (i = 0) {
			// WAIT FOR BEGINNING OF EVENT
			if (SOMETHING == TRUE) {
				i = 1;
			}
			std::this_thread::sleep_for(std::chrono::milliseconds(20));
		}
		while ((!exit_flag) && (!event_over)) {
			if ((pressed == 1) && (!event_over))
			{
				if (trigger2 == true) {
					// RESPOND TO TRIGGER
					trigger2 = false;
				}
				if (trigger1 == true) {
					// RESPOND TO TRIGGER
					trigger1 = false;
				}
				if (OTHER_THING == TRUE) {

					click(click_loc);
					click(click_loc);
					click(click_loc);

				}
				std::this_thread::sleep_for(std::chrono::milliseconds(100));
			}
			if ((pressed == 0) && (!event_over))
			{
				if (THING2 == FALSE) {
					pressed = 1;
					if (THING4 == FALSE){
						event_over = true;
					}
				}
				std::this_thread::sleep_for(std::chrono::milliseconds(20));
			}
		}
		std::cout << "Thread 'Command Thread' Suspending..." << endl;
		while (event_over) {
		std::this_thread::sleep_for(std::chrono::milliseconds(1000));	
		// WOULD WAIT FOR EVENT RESET IF THREAD DIDN'T FORCE CLOSE MAIN PROGRAM
		}
	}
	std::cout << "Command Thread' stopped" << endl;
}

int get_Bitmap(int GxpSx, int GypSy, HDC& hdcMemory, int width, int height) { 				//  
	hdcMemory = CreateCompatibleDC(hdcSource);							// 
	HBITMAP hBitmap = CreateCompatibleBitmap(hdcSource, width, height);
	HBITMAP hBitmapOld = (HBITMAP)SelectObject(hdcMemory, hBitmap);
	if (!BitBlt(hdcMemory, 0, 0, width, height, hdcSource, GxpSx, GypSy, CAPTUREBLT | SRCCOPY)) {
		cout << "BitBlt failed!" << endl;
	}

	//clean up
	DeleteObject(hBitmapOld);
	DeleteObject(hBitmap);
	ReleaseDC(NULL, hdcSource);

	return 0;
}


void update_Bitmap(POINT GWindowO, // WINDOW ORIGIN -- WILL BE ADDED TO POINT S TO GET CLIP ORIGIN
	POINT S, // X OFFSET OF CLIP STARTING POINT FROM WINDOW ORIGIN
	HDC& hdcMemory, // NAME OF STORAGE LOCATION FOR THIS CLIP
	bool& exit_flag,
	int width, // WIDTH OF CLIP
	int height, // HEIGHT OF CLIP
	int refresh // TIME IN MILLISECONDS BETWEEN RUNS
) {
	while ((!exit_flag)) {
		get_Bitmap(S.x + GWindowO.x, S.y + GWindowO.y, hdcMemory, width, height);
		std::this_thread::sleep_for(std::chrono::milliseconds(refresh));
	}
	std::cout << "thread: update Bitmap stopped" << endl;
	return;
}



int main()
{

	// VARIABLES AND JUNK
	
	bool exit_flag = false;
	bool event_over = false;



	htrgWnd = get_window();
	HDC hDC = GetDC(htrgWnd); // THIS GRABS THE ORIGIN OF THE  WINDOW ON STARTUP TO BE USED THROUGHOUT
	ClientToScreen(htrgWnd, &GwindowO);
	cast_button.x = cast_button.x + GwindowO.x;
	cast_button.y = cast_button.y + GwindowO.y;
	ReleaseDC(htrgWnd, hDC);
	get_Bitmap(GwindowO.x + tmeter.x, GwindowO.y + tmeter.y, hdcMemory, width, height);
	get_Bitmap(GwindowO.x, GwindowO.y, hdcMemoryFULL, 432, 934);

	exit_flag = false;
	event_over = false;
	if (hdcMemory == NULL) {
		std::cout << "hdcMemory is NULL" << endl;
	}
	if (hdcMemoryFULL == NULL) {
		std::cout << "hdcMemoryFULL is NULL" << endl;
	}
	std::thread t_Bitmap_FS(update_Bitmap, GwindowO, no_offset, ref(hdcMemoryFULL), ref(exit_flag), 432, 934, 1000);
	std::thread t_Bitmap(update_Bitmap, GwindowO, tmeter, ref(hdcMemory), ref(exit_flag), width, height, 25);
	std::thread t_F_Cmds(F_Cmds, ref(exit_flag), ref(event_over));

	while (!exit_flag) {
		
		std::this_thread::sleep_for(std::chrono::milliseconds(1000));
		
		while (event_over)  {
			
			std::this_thread::sleep_for(std::chrono::milliseconds(1000));
			
			if (GetAsyncKeyState('0')) { // WATCH FOR REQUESTED EARLY EXIT
				
				exit_flag = true; // FORCE ALL THREADS TO EXIT
				
			}
		}
	}
}
Last edited on
You should use std::atomic for the exit_flag variable to avoid data races - its being used in both threads and in the main thread. Also, you're giving t_Bitmap_FS and t_Bitmap the same exit_flag variable. This means setting it to true will exit both threads.

I assume you want separate flags for each thread so that they don't both stop at the same time when you set one of the exit flag variables to true.
I'm not familiar with std::atomic I'll look into it and implement it.

As far as the exit_flag goes, it's really only to exit all threads in the case the user (me) wants the program to terminate. Ideally if I can get this set up, it would go through a set number of iterations and self exit (haven't got that far yet since I can't figure out how to loop it).

As it stands, if t_F_Cmds ends (whether it's by allowing it to reach the end of it's code, sending an exit_flag unique to that thread, etc..) the int main() thread is force exited as well.

VS throws 'abort() has been called' and shows an exception on _SD terminate(); in the std::thread file. Even trying to ignore the message, int main(), t_Bitmap, and t_Bitmap_FS all finish and close as though I exited them on purpose.


1
2
3
4
5
	~thread() noexcept
		{	// clean up
		if (joinable())
			_STD terminate();
		}


In no way am I able to get t_F_Cmds to exit without ending every thread. Am I missing or misunderstanding something?

I'm pulling my hair out over just trying to "reset" my active threads so I can loop through the jobs that need done lol.

Last edited on
The code isn't runnable and gives a lot of errors, so there's no real way for me to see what issue you're facing.

Just seems like your code is doing something it shouldn't, causing it to crash. Would implement atomic just to rule out race conditions.
Sorry, I had to remove all parts that were specific to the program I was running it along side.

Here's a compliable version of the code that yields the same problem when run.

Once t_F_Cmd finishes, it forces everything to close. I can't put any code in to execute after thread t_F_Cmd exits.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
#include <chrono>
#include <cstring>
#include <fstream>
#include <iostream>
#include <thread>
#include <windows.h>

using namespace std;

int Rn(int center, int width) {
	int n = width + 1;
	int m = 0 - width / 2;
	int r = rand() % n + m + center;
	return r;
}

void PS_activate(bool& PS_active, bool& EVT_TYPE_2, bool& exit_flag,
 	bool& event_over, HDC& hdcMemory, HDC& hdcMemoryFULL) {

	event_over = false;
	while ( Rn(50, 100) < 25 || Rn(50, 100) > 75 ) {

	}

	while ((!exit_flag) && (!event_over)) {

		if ( Rn(50, 100) > 50) {
			PS_active = true;
		}
		if (Rn(50, 100) < 50 ) {
			EVT_TYPE_2 = true;
		}
		if ( Rn(50, 100) > 90 && Rn(50, 100) < 10 ) {
			event_over = true;
		}
		std::this_thread::sleep_for(std::chrono::milliseconds(25));
	}
	std::cout << "PS Flag Stopping..." << endl;

}

void F_Cmd(bool& exit_flag, bool& event_over, POINT GWindowO, HDC& hdcMemory,
	 HDC& hdcMemoryFULL) {

	// INITIALIZING VARIABLES
	bool PS_active = false;
	bool EVT_TYPE_2 = false;



	int pressed = 1;
	int i = 0;


	std::thread t_PS_Flag(PS_activate, ref(PS_active), ref(EVT_TYPE_2), ref(exit_flag),
	 ref(event_over), ref(hdcMemory), ref(hdcMemoryFULL));


	i = 0;
	pressed = 1;


	while (i = 0) {

		if (Rn(50, 100) < 25) {
			i = 1;
		}
		std::this_thread::sleep_for(std::chrono::milliseconds(20));
	}
	while ((!exit_flag) && (!event_over)) {
		if ((pressed == 1) && (!event_over))
		{
			if (PS_active == true) {

				PS_active = false;

			}
			if (EVT_TYPE_2 == true) {

				EVT_TYPE_2 = false;
			}
			if ( Rn(50, 100) > 75 ) { 

			}

		}
		if ((pressed == 0) && (!event_over)) 
		{

		}

	}

}

int get_Bitmap(int GxpSx, int GypSy, HDC& hdcMemory, int width, int height) {
	HDC hdcSource = GetDC(NULL);
	hdcMemory = CreateCompatibleDC(hdcSource);
	HBITMAP hBitmap = CreateCompatibleBitmap(hdcSource, width, height);
	HBITMAP hBitmapOld = (HBITMAP)SelectObject(hdcMemory, hBitmap);
	if (!BitBlt(hdcMemory, 0, 0, width, height, hdcSource, GxpSx, GypSy, CAPTUREBLT | SRCCOPY)) {
		cout << "BitBlt failed!" << endl;
	}

	//clean up
	DeleteObject(hBitmapOld);
	DeleteObject(hBitmap);
	ReleaseDC(NULL, hdcSource);

	return 0;
}


void update_Bitmap(POINT GWindowO,
	POINT S,
	HDC& hdcMemory,
	bool& exit_flag,
	int width,
	int height,
	int refresh 
) {
	while ((!exit_flag)) {
		get_Bitmap(S.x + GWindowO.x, S.y + GWindowO.y, hdcMemory, width, height);
		std::this_thread::sleep_for(std::chrono::milliseconds(refresh));
	}
	std::cout << "thread: update Bitmap stopped" << endl;
	return;
}

int main()
{

	HDC hdcMemory = NULL;
	HDC hdcMemoryFULL = NULL;

	bool exit_flag = false;
	bool event_over = false;

	POINT GwindowO = { 0,0 };
	POINT no_offset = { 0,0 };

	int GwindowOx = 0;
	int GwindowOy = 0;

	int input = NULL;

	HDC hDC = GetDC(NULL);
	ReleaseDC(NULL, hDC);
	get_Bitmap(0, 0, hdcMemory, 50, 50);
	get_Bitmap(0, 0, hdcMemoryFULL, 100, 100);

	exit_flag = false;
	event_over = false;

	if (hdcMemory == NULL) {
		std::cout << "hdcMemory is NULL" << endl;
	}
	if (hdcMemoryFULL == NULL) {
		std::cout << "hdcMemoryFULL is NULL" << endl;
	}
	std::thread t_Bitmap_FS(update_Bitmap, GwindowO, no_offset, ref(hdcMemoryFULL), ref(exit_flag), 100, 100, 1000);
	std::thread t_Bitmap(update_Bitmap, GwindowO, no_offset, ref(hdcMemory), ref(exit_flag), 50, 50, 25);
	std::thread t_F_Cmd(F_Cmd, ref(exit_flag), ref(event_over), GwindowO, ref(hdcMemory), ref(hdcMemoryFULL));

	while (!exit_flag) {

		while (event_over) {

			std::this_thread::sleep_for(std::chrono::milliseconds(2000));

			std::cout << "" << endl;
			std::cout << "AGAIN?" << endl;
			std::cout << "" << endl;
			std::cin >> input;
			system("PAUSE");
			system("PAUSE");
			std::cin >> input;
			std::cout << "" << endl;

			event_over = false;


		}
	}
}
> Well I was hoping not to show how inexperienced I am at code writing
Then you need to stop trying to use threads.
https://wiki.c2.com/?ThreadsConsideredHarmful

You're basically trying to cross a mine field in a wheel chair in the dark.

The first big no-no in your code is trying to use the Win32 GUI in a thread other than the main thread. This isn't going to work - it is not a thread-safe API.

There seems little need for threads in your program to be honest, given the number of times you use "std::this_thread::sleep_for" just to bum around waiting for something else to happen. You may as well just call things sequentially and it will be done when it's done.

If you use wait/sleep_for in thread related code, then it's almost certain that something isn't right. You should have a look at condition variables/futures:

http://www.cplusplus.com/reference/future/
http://www.cplusplus.com/reference/condition_variable/condition_variable/
Salem - I appreciate the advice and that was an interesting read. I will probably take your advice and go rewrite what I have in a linear fashion. I hate to do it, but if I can't get the threading to work properly there's no use pursuing it.

Just to be clear, that runnable code I put there has 90% of the actual code stripped away. The program that I'm writing is meant for speed in monitoring and reacting to changes on the screen coming from another program.

The wait/sleep remnants in the code posted above are only present when necessary in my code to slow down virtual mouse click and key input signals so they can be processed by the slower program.

The number of pixels to be looked at for color range is pretty high and using a linear approach quickly slows down reaction time when multiple events needed to be looked for at the same time. Threading seemed like it would be the perfect solution where I can have each thread independently yet simultaneously handling a different type of event occurring.

When reaction time and speed are most important, threading looked too good to not try.

I appreciate everyone's help and feedback!
The reason for your crash is that the F_Cmd function is creating a thread, but it returns before the thread it created was finished executing.

You want to wait for the thread to finish executing via .join():

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
void F_Cmd(std::atomic<bool>& exit_flag, std::atomic<bool>& event_over, POINT GWindowO, HDC& hdcMemory,
	HDC& hdcMemoryFULL) 
{

	// INITIALIZING VARIABLES
	std::atomic<bool> PS_active = false;
	std::atomic<bool> EVT_TYPE_2 = false;



	int pressed = 1;
	int i = 0;


	std::thread t_PS_Flag(PS_activate, ref(PS_active), ref(EVT_TYPE_2), ref(exit_flag),
		ref(event_over), ref(hdcMemory), ref(hdcMemoryFULL));

	i = 0;
	pressed = 1;


	while (i == 0) {

		if (Rn(50, 100) < 25) {
			i = 1;
		}
		std::this_thread::sleep_for(std::chrono::milliseconds(20));
	}
	while ((!exit_flag) && (!event_over)) {
		if ((pressed == 1) && (!event_over))
		{
			if (PS_active == true) {

				PS_active = false;

			}
			if (EVT_TYPE_2 == true) {

				EVT_TYPE_2 = false;
			}
			if (Rn(50, 100) > 75) {

			}

		}
		if ((pressed == 0) && (!event_over))
		{

		}

	}

	if(t_PS_Flag.joinable()) t_PS_Flag.join(); //Now the function won't exit early
}
Last edited on
Thanks everyone for your responses. I'm rewriting the program in a linear fashion, so this question is no longer relevant.

I really appreciate you all for taking the time to answer my question!
Topic archived. No new replies allowed.