Still celebrating National IT Professionals Day with 3 months of free Premium Membership. Use Code ITDAY17

x
?
Solved

Thread started with AfxBeginThread() hogging CPU

Posted on 2012-12-23
8
Medium Priority
?
765 Views
Last Modified: 2012-12-28
I have CDialog based modal dialog that invokes a fairly lengthy processing step using the following call:

Thr=AfxBeginThread(MyFunction, pPam, THREAD_PRIORITY_LOWEST);

The thread starts up and runs nicely to completion.   However I have a CProgressCtrl on the invoking modal dialog that I want to update while the lengthy process is happening.

I have a timer set in the dialog that calls me every 100 ms.  At that time I attempt to set the progress bar using SetPos().  The problem I have is that I have to call Sleep() in the spawned thread in order to give the dialog a chance to update the progress bar.

I would have thought that the dialog would have gotten some cycles especially when I used  THREAD_PRIORITY_LOWEST for the spawned thread.

Is the main thread expected to get no cycles unless the spawned thread does some sleeping?

I'm running on a reasonable machine (Intel i5).
0
Comment
Question by:allanephillips
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 4
  • 3
8 Comments
 
LVL 25

Expert Comment

by:Tony Giangreco
ID: 38717174
Have you tried limiting the time alloted to the sleep command?
0
 

Author Comment

by:allanephillips
ID: 38717187
Yes I have.  It seems as though I have to give it about 100 ms before things work.  That seems like a lot of time.
0
 
LVL 9

Expert Comment

by:Orcbighter
ID: 38721753
Can you show some of the code in the MyFunction process?
0
Windows Server 2016: All you need to know

Learn about Hyper-V features that increase functionality and usability of Microsoft Windows Server 2016. Also, throughout this eBook, you’ll find some basic PowerShell examples that will help you leverage the scripts in your environments!

 

Author Comment

by:allanephillips
ID: 38721907
Here is the complete source.  It simply does symtax highlighting of some XML.  Of course it's fairly CPU intensive.


UINT __cdecl MyFunction(LPVOID pParam)
{
	Thread_t *pThread = (Thread_t *)pParam;

	pThread->lPos = 0;
	bool bReturn = MySyntaxHighlightFile(pThread->sInputName, pThread->sOutputName, &pThread->lPos);

	return 1;
}


// Copy the input file to the output file inserting RTF markup to syntax color the XML.
bool MySyntaxHighlightFile(const wchar_t *sInputFileName, const wchar_t *sOutputFileName, long *lPos)
{
	FILE *fpIn;
	_wfopen_s(&fpIn, sInputFileName, L"rb");
	if (!fpIn)
		return false;

	FILE *fpOut;
	_wfopen_s(&fpOut, sOutputFileName, L"wb");
	if (!fpIn)
		return false;
	char sTag[1000];
	iTagIndex = 0;
	sTag[0] = 0;

	long long i64FileLength = _filelengthi64(_fileno(fpIn));
	long long i64Step = (long long) ((i64FileLength / 60.0) + 0.5);
	long long i64CurPos = 0;

	bool bInTag = false;
	bool bInAttributes = false;
	bool bAttrValue = false;

	char sBOM[3];
	fread(sBOM, 1, 3, fpIn);				// Swallow the byte-order-mark.

	fputs("{\\rtf1{{\\colortbl ;"
			"\\red0\\greeen0\\blue0;"				// Regular text - black	= 1
			"\\red255\\green0\\blue0;"				// ObjectTag color		= 2
			"\\red0\\green128\\blue255;"			// Tag color			= 3
			"\\red0\\green0\\blue255;"				// Quote color			= 4
			"\\red30\\green150\\blue30;"			// Attribute name color	= 5
			"\\red0\\green0\\blue255;"				// Angle color			= 6
			"\\red192\\green192\\blue192;"			// Comment color		= 7
			"\\red0\\green128\\blue255;}"			// PI color				= 8
			, fpOut);

	unsigned iChar;
	while ((iChar = fgetc(fpIn)) != EOF)
	{
		i64CurPos++;
		switch (iChar)
		{
		case '\r':
			break;						// Don't need carriage returns.

		case '\n':
			fwrite("\\par ", 1, 5, fpOut);
			break;

		case '{':
		case '}':
		case '\\':
			fputc('\\', fpOut); 
			fputc(iChar, fpOut);
			break;

		case '<':
			{
				fputs("\\cf6 ", fpOut);
				fputc(iChar, fpOut);
				int iNextChar = fgetc(fpIn);
				i64CurPos++;
				switch (iNextChar)
				{
				case '!':
					fputs("\\cf7 ", fpOut);
					fputc(iNextChar, fpOut);
					break;

				case '?':
					fputs("\\cf8 ", fpOut);
					fputc(iNextChar, fpOut);
					break;

				case '/':
					fputc(iNextChar, fpOut);
					bInTag = true;
					iTagIndex = 0;
					break;

				default:
					bInTag = true;
					iTagIndex = 0;
					sTag[iTagIndex++] = iNextChar;
					sTag[iTagIndex] = 0;
				}
			}
			continue;

		case '>':
			if (bInTag)
			{
				OutputTag(sTag, fpOut);
				bInTag = false;
			}
			bInAttributes = false;
			fputs("\\cf6 ", fpOut);
			fputc(iChar, fpOut);
			fputs("\\cf1 ", fpOut);
			break;

		case ' ':
			if (bInTag)
			{
				OutputTag(sTag, fpOut);
				bInTag = false;
				bInAttributes = true;
			}
			fputc(iChar, fpOut);
			break;

		case '"':
			if (bInAttributes)
			{
				if (bAttrValue)
					fputs("\\cf4 \"\\cf5 ", fpOut);		// Closing quote for attribute value.
				else
					fputs("\\cf4 \"\\cf1 ", fpOut);		// Opening quote for attribute value.
				bAttrValue = !bAttrValue;
			}
			break;

		default:
			if (bInTag)
			{
				sTag[iTagIndex++] = iChar;
				sTag[iTagIndex] = 0;
			}
			else if (iChar <= 0x7F)
				fputc(iChar, fpOut);
			else
			{
				// Now we have to convert this UTF8 extended character to Unicode.
				unsigned char sUtf8[11];
				int iIndex = 0;
				sUtf8[iIndex++] = iChar;

				// Read in all the bytes of this unicode character.
				while ((iChar = fgetc(fpIn)) != EOF)
				{
					if ((iChar < 0x80) || (iChar > 0xBF) || (iIndex > 9))
						break;
					sUtf8[iIndex++] = iChar;
					i64CurPos++;
				}
				i64CurPos--;
				ungetc(iChar, fpIn);

				sUtf8[iIndex] = 0;
				wchar_t *sUnicode = ATW::UTF82Wide((char *)sUtf8);

				fprintf(fpOut, "\\u%d ?", *sUnicode);	// Extended character.
			}
		}
		if ((i64CurPos % i64Step) == 0)
			(*lPos)++;
	}

	fputs("}", fpOut);

	fclose(fpIn);
	fclose(fpOut);

	return true;
}

Open in new window

0
 
LVL 9

Expert Comment

by:Orcbighter
ID: 38722778
Sorry, I meant the section of code where you call sleep, and the section of code where you pass information to update the progress bar.
0
 

Author Comment

by:allanephillips
ID: 38723503
The code that updates the progress bar is simply:

    mProgressBar.StepIt();
    mProgressBar.UpdateWindow();

Open in new window


inside OnTimer().

The Sleep() call was at this point in the code I sent:

     if ((i64CurPos % i64Step) == 0)
      {
	  (*lPos)++;
          Sleep(100);
       }

Open in new window


This does actually work but 100 ms seems like a lot of time effectively doing nothing.
0
 
LVL 9

Accepted Solution

by:
Orcbighter earned 300 total points
ID: 38726408
Okay, from what I can see and imply from the code you haven't supplied is this:
1. You start a thread to do some stuff which is compute-intensive, and seems to swallow up your CPU. Your timer events might be being queued as a result.
2. You have a timer that periodically calls progress.StepIt() to advance the visual display bar.
3. You found that you had to add a Sleep command to the compute-bound thread in order for the display update to get enough time to update the display, and that you had to increase the amount of time in the Sleep for it to work.

On point 1: Yes, without some kind of break in the thread process, it would consume CPU resources.
On point 3: My thoughts are that, if the timer event does not helpfully coincide with the Sleep command, it would not get enough time to act, thus you had to keep increasing the Sleep time in the thread, ie, the Sleep window in the background thread does not coincide with the window of the timer event.

Suggestions:
1. Change the Sleep command in the thread function back to 10 ms (the smallest effective amount of time you can get).
Now that the Sleep is so small, you can afford to sleep more times (more than i64CurPos % i64Step times). Thus your sleep will relinquish control for far shorter periods of time, but the increased frequency of the Sleep calls means other threads might get a look in.

2. If point 1 fixes the problem, good. If not, maybe consider another option rather than a timer.Instead of the timer, use a Windows Event and send that to an event handler in the dialog that updates the progress bar.
An additional thought is that the timer event that updates the Progress bar has no real link with the position of the file being processed. The time fires and just arbitrarily increments the progress bar. An event message might allow you a more connected link with the stepIt command by adding a parameter to the event message (but that might be version 2, yes ? :-) )
0
 

Author Closing Comment

by:allanephillips
ID: 38727305
I'm accepting this even though I have not been able to make use of the information.  It feels as though threads are still useless on Windows so I've just dropped the separate thread.

When one spins off a thread with the priority set to "THREAD_PRIORITY_LOWEST" and the other thread(s) get no cycles without doing a call to Sleep() then it's obvious that threads have not been implemented in Windows in a way that's useful yet.
0

Featured Post

New feature and membership benefit!

New feature! Upgrade and increase expert visibility of your issues with Priority Questions.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

This article shows how to make a Windows 7 gadget that accepts files dropped from the Windows Explorer.  It also illustrates how to give your gadget a non-rectangular shape and how to add some nifty visual effects to text displayed in a your gadget.…
What my article will show is if you ever had to do processing to a listbox without being able to just select all the items in it. My software Visual Studio 2008 crystal report v11 My issue was I wanted to add crystal report to a form and show…
This is Part 3 in a 3-part series on Experts Exchange to discuss error handling in VBA code written for Excel. Part 1 of this series discussed basic error handling code using VBA. http://www.experts-exchange.com/videos/1478/Excel-Error-Handlin…
Add bar graphs to Access queries using Unicode block characters. Graphs appear on every record in the color you want. Give life to numbers. Hopes this gives you ideas on visualizing your data in new ways ~ Create a calculated field in a query: …

715 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question