Jump to content
  • Advertisement
Sign in to follow this  
assainator

win32 threadpool wait problems

This topic is 2645 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hello everyone,

I've been trying to use the win32 threadpool as creating and destroying threads add's a to high performance penalty(~90 milliseconds) to create and destroy the threads each itteration of the loop I'm going through.

I have been able to create the threadpool and start a 100 threads (I'm just trying to get a threadpool to work before I implement it into my project) all of which finish.

The problem is wait for the threads. I need all of the threads to finish before the application can continue. I have tried using WaitForThreadPoolWaitCallbacks, but for some reason execution won't wait untill all threads have finished.

Here is my minimal code, I marked the waiting related code.

[source]void __stdcall WorkFunc(PTP_CALLBACK_INSTANCE Instance, void* Parameter, PTP_WORK Work)
{
printf("Hello from thread %d\n", (unsigned int)Parameter);
}

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
void __stdcall WaitFunc(PTP_CALLBACK_INSTANCE Instance, void* Parameter, PTP_WAIT Wait, TP_WAIT_RESULT WaitResult)
{
return;
}
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!


void ThreadPoolTest()
{
// The work thread
PTP_WORK_CALLBACK worker = WorkFunc;
PTP_WAIT_CALLBACK waiter = WaitFunc;

// Create a callback environment
TP_CALLBACK_ENVIRON CallbackEnviron;
InitializeThreadpoolEnvironment(&CallbackEnviron);

// Create a pool
PTP_POOL pool = CreateThreadpool(NULL);

// Check if the thread pool is valid
if(!pool)
{
printf("CreateThreadPool failed!");
return;
}

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// Create the wait object
PTP_WAIT Wait = CreateThreadpoolWait(waiter, NULL, &CallbackEnviron);
if(!Wait)
{
printf("Creating Wait failed!\n");
CloseThreadpool(pool);
return;
}

SetThreadpoolWait(Wait, NULL, NULL);
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

// Get logical processor count
SYSTEM_INFO sys_info;
GetSystemInfo(&sys_info);

// Set Thread pool limits
SetThreadpoolThreadMaximum(pool, sys_info.dwNumberOfProcessors);
int TP_minThread_success = SetThreadpoolThreadMinimum(pool, 1);

// Check if everything went alright
if(!TP_minThread_success)
{
CloseThreadpool(pool);
printf("Setting thread pool limits failed!");
return;
}

// associate callback environment with the thread pool
SetThreadpoolCallbackPool(&CallbackEnviron, pool);

// Run 100 threads
for (unsigned int i = 0; i < 100; i++)
{
// Create work
PTP_WORK work = CreateThreadpoolWork(worker, (void*)i, &CallbackEnviron);

// Submit work
SubmitThreadpoolWork(work);
}

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
WaitForThreadpoolWaitCallbacks(Wait, false);
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
}

int main()
{
printf("Press the return key to start");
getchar();

unsigned int startTime = GetTickCount();
ThreadPoolTest();
unsigned int endTime = GetTickCount();

printf("\n\nIt took %d ms.\n", (endTime - startTime));

getchar();
}
[/source]

Thanks in advance for any help.

Share this post


Link to post
Share on other sites
Advertisement
Thanks for pointing that out, I have added a event and passed as you can see in my updated code below. But the code still doesn't wait for all the threads to finish.
I tested this example: http://msdn.microsof...s686980(v=vs.85).aspx
If I only run the DemoNewRegisterWait() function, I get 5 times "MyWaitCallback: wait is over." as I would expect. The strange thing is, if I remove the call to sleep(500), I get no output at all.

And I don't want to be forced to put a sleep call into my code as the entire reason I wanted a threadpool was performance, and putting a sleep call in my code (which may result into waiting longer then is necessary) would hardly increase this.

I have tried moving the calls to 'SetThreadPoolWait' and 'SetEvent' to the loop that fires the threads but I get the same result.

Code: I marked the modified part.
[source]
void __stdcall WorkFunc(PTP_CALLBACK_INSTANCE Instance, void* Parameter, PTP_WORK Work)
{
printf("Hello from thread %d\n", (unsigned int)Parameter);
}

void __stdcall WaitFunc(PTP_CALLBACK_INSTANCE Instance, void* Parameter, PTP_WAIT Wait, TP_WAIT_RESULT WaitResult)
{
return;
}


void ThreadPoolTest()
{
// The work thread
PTP_WORK_CALLBACK worker = WorkFunc;
PTP_WAIT_CALLBACK waiter = WaitFunc;

// Create a callback environment
TP_CALLBACK_ENVIRON CallbackEnviron;
InitializeThreadpoolEnvironment(&CallbackEnviron);

// Create a pool
PTP_POOL pool = CreateThreadpool(NULL);

// Check if the thread pool is valid
if(!pool)
{
printf("CreateThreadPool failed!");
return;
}

// Create the wait object
PTP_WAIT Wait = CreateThreadpoolWait(waiter, NULL, &CallbackEnviron);
if(!Wait)
{
printf("Creating Wait failed!\n");
CloseThreadpool(pool);
return;
}

!!!!!!!!!!!!!!!!!!!!!
// Create the event
HANDLE evt = CreateEvent(NULL, FALSE, FALSE, NULL);
if (!evt)
{
CloseThreadpool(pool);
return;
}

// Set wait
SetThreadpoolWait(Wait, evt, NULL);
SetEvent(evt);
!!!!!!!!!!!!!!!!!!!!!


// Get logical processor count
SYSTEM_INFO sys_info;
GetSystemInfo(&sys_info);

// Set Thread pool limits
SetThreadpoolThreadMaximum(pool, sys_info.dwNumberOfProcessors);
int TP_minThread_success = SetThreadpoolThreadMinimum(pool, 1);

// Check if everything went alright
if(!TP_minThread_success)
{
CloseThreadpool(pool);
printf("Setting thread pool limits failed!");
return;
}

// associate callback environment with the thread pool
SetThreadpoolCallbackPool(&CallbackEnviron, pool);

// Run 100 threads
for (unsigned int i = 0; i < 100; i++)
{
// Create work
PTP_WORK work = CreateThreadpoolWork(worker, (void*)i, &CallbackEnviron);

// Submit work
SubmitThreadpoolWork(work);
}

WaitForThreadpoolWaitCallbacks(Wait, false);
}


int main()
{
printf("Press the return key to start");
getchar();

unsigned int startTime = GetTickCount();
ThreadPoolTest();
unsigned int endTime = GetTickCount();

printf("\n\nIt took %d ms.\n", (endTime - startTime));

getchar();
}
[/source]

Share this post


Link to post
Share on other sites
it seems you've misunderstood what the 'Wait' in these functions refers to. It's not a 'wait until there's no more work, then call WaitFunction', it's a 'wait until the handle passed to SetThreadpoolWait is signalled, then call WaitFunction'. It's basically the new money equivalent of RegisterWaitForSingleObject.

To wait for all the work to finish, you can use a cleanup group. The structure would be CreateThreadpoolCleanupGroup(), SetThreadpoolCallbackCleanupGroup(&CallbackEnviron, pCleanup, ...), and then the CloseThreadpoolCleanupGroupMembers(, FALSE, ) which will wait for all outstanding work to complete. Or, in code

[source]
void __stdcall WorkFunc(PTP_CALLBACK_INSTANCE Instance, void* Parameter, PTP_WORK Work)
{
printf("Hello from thread %d\n", (unsigned int)Parameter);
}

void ThreadPoolTest()
{
// The work thread
PTP_WORK_CALLBACK worker = WorkFunc;
// Create a callback environment
TP_CALLBACK_ENVIRON CallbackEnviron;
InitializeThreadpoolEnvironment(&CallbackEnviron);
// Create a pool
PTP_POOL pool = CreateThreadpool(NULL);
// Check if the thread pool is valid
if(!pool)
{
printf("CreateThreadPool failed!");
return;
}
// Create the cleanup group
PTP_CLEANUP_GROUP pCleanup = CreateThreadpoolCleanupGroup();
if(!pCleanup)
{
printf("Creating cleanup group failed!\n");
CloseThreadpool(pool);
return;
}
// associate callback environment with the thread pool
SetThreadpoolCallbackPool(&CallbackEnviron, pool);
// and the cleanup group with the callback environment
SetThreadpoolCallbackCleanupGroup(&CallbackEnviron, pCleanup, NULL);
// !!!!!!!!!!!!!!!!!!!!!
// Get logical processor count
SYSTEM_INFO sys_info;
GetSystemInfo(&sys_info);
// Set Thread pool limits
SetThreadpoolThreadMaximum(pool, sys_info.dwNumberOfProcessors);
int TP_minThread_success = SetThreadpoolThreadMinimum(pool, 1);
// Check if everything went alright
if(!TP_minThread_success)
{
CloseThreadpoolCleanupGroup(pCleanup);
CloseThreadpool(pool);
printf("Setting thread pool limits failed!");
return;
}
// Run 100 work items, not 100 threads
for (unsigned int i = 0; i < 100; i++)
{
// Create work
PTP_WORK work = CreateThreadpoolWork(worker, (void*)i, &CallbackEnviron);
// Submit work
SubmitThreadpoolWork(work);
}
// waits for all work items to finish and frees all the PTP_WORK structs allocated
// The FALSE stops work that hasn't been scheduled from being canceled
CloseThreadpoolCleanupGroupMembers(pCleanup, FALSE, NULL);
// other object cleanup
CloseThreadpoolCleanupGroup(pCleanup);
CloseThreadpool(pool);
}

int main()
{
printf("Press the return key to start");
getchar();
unsigned int startTime = GetTickCount();
ThreadPoolTest();
unsigned int endTime = GetTickCount();
printf("\n\nIt took %lu ms.\n", (endTime - startTime));
getchar();
return 0;
}
[/source]

This may shed more light on how it all fits together, at least more than the wall-of-text intro page and sparse function pages on the main MSDN.

Share this post


Link to post
Share on other sites
Thanks adeyblue, that did the trick!

When using a mutex one uses WaitForSingleObject, so I assumed I had to use something with wait for a threadpool as well.

Thanks again!

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!