Walkthrough: Adapting Existing Code to Use Lightweight Tasks
This topic shows how to adapt existing code that uses the Windows API to create and execute a thread to use a lightweight task.
A lightweight task is a task that you schedule directly from a concurrency::Scheduler or concurrency::ScheduleGroup object. Lightweight tasks are useful when you adapt existing code to use the scheduling functionality of the Concurrency Runtime.
Prerequisites
Before you start this walkthrough, read the topic Task Scheduler.
Example
The following example illustrates typical usage of the Windows API to create and execute a thread. This example uses the CreateThread function to call the MyThreadFunction
on a separate thread.
Initial code
// windows-threads.cpp
#include <windows.h>
#include <tchar.h>
#include <strsafe.h>
#define BUF_SIZE 255
DWORD WINAPI MyThreadFunction(LPVOID param);
// Data structure for threads to use.
typedef struct MyData {
int val1;
int val2;
} MYDATA, *PMYDATA;
int _tmain()
{
// Allocate memory for thread data.
PMYDATA pData = (PMYDATA) HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY, sizeof(MYDATA));
if( pData == NULL )
{
ExitProcess(2);
}
// Set the values of the thread data.
pData->val1 = 50;
pData->val2 = 100;
// Create the thread to begin execution on its own.
DWORD dwThreadId;
HANDLE hThread = CreateThread(
NULL, // default security attributes
0, // use default stack size
MyThreadFunction, // thread function name
pData, // argument to thread function
0, // use default creation flags
&dwThreadId); // returns the thread identifier
if (hThread == NULL)
{
ExitProcess(3);
}
// Wait for the thread to finish.
WaitForSingleObject(hThread, INFINITE);
// Close the thread handle and free memory allocation.
CloseHandle(hThread);
HeapFree(GetProcessHeap(), 0, pData);
return 0;
}
DWORD WINAPI MyThreadFunction(LPVOID lpParam)
{
PMYDATA pData = (PMYDATA)lpParam;
// Use thread-safe functions to print the parameter values.
TCHAR msgBuf[BUF_SIZE];
StringCchPrintf(msgBuf, BUF_SIZE, TEXT("Parameters = %d, %d\n"),
pData->val1, pData->val2);
size_t cchStringSize;
StringCchLength(msgBuf, BUF_SIZE, &cchStringSize);
DWORD dwChars;
WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), msgBuf, (DWORD)cchStringSize, &dwChars, NULL);
return 0;
}
This example produces the following output.
Parameters = 50, 100
The following steps show how to adapt the code example to use the Concurrency Runtime to perform the same task.
To adapt the example to use a lightweight task
- Add a
#include
directive for the header file concrt.h.
#include <concrt.h>
- Add a
using
directive for theconcurrency
namespace.
using namespace concurrency;
- Change the declaration of
MyThreadFunction
to use the__cdecl
calling convention and to returnvoid
.
void __cdecl MyThreadFunction(LPVOID param);
- Modify the
MyData
structure to include a concurrency::event object that signals to the main application that the task has finished.
typedef struct MyData {
int val1;
int val2;
event signal;
} MYDATA, *PMYDATA;
- Replace the call to
CreateThread
with a call to the concurrency::CurrentScheduler::ScheduleTask method.
CurrentScheduler::ScheduleTask(MyThreadFunction, pData);
- Replace the call to
WaitForSingleObject
with a call to the concurrency::event::wait method to wait for the task to finish.
// Wait for the task to finish.
pData->signal.wait();
Remove the call to
CloseHandle
.Change the signature of the definition of
MyThreadFunction
to match step 3.
void __cdecl MyThreadFunction(LPVOID lpParam)
- At the end of the
MyThreadFunction
function, call the concurrency::event::set method to signal to the main application that the task has finished.
pData->signal.set();
- Remove the
return
statement fromMyThreadFunction
.
Completed code
The following completed example shows code that uses a lightweight task to call the MyThreadFunction
function.
// migration-lwt.cpp
// compile with: /EHsc
#include <windows.h>
#include <tchar.h>
#include <strsafe.h>
#include <concrt.h>
using namespace concurrency;
#define BUF_SIZE 255
void __cdecl MyThreadFunction(LPVOID param);
// Data structure for threads to use.
typedef struct MyData {
int val1;
int val2;
event signal;
} MYDATA, *PMYDATA;
int _tmain()
{
// Allocate memory for thread data.
PMYDATA pData = (PMYDATA) HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY, sizeof(MYDATA));
if( pData == NULL )
{
ExitProcess(2);
}
// Set the values of the thread data.
pData->val1 = 50;
pData->val2 = 100;
// Create the thread to begin execution on its own.
CurrentScheduler::ScheduleTask(MyThreadFunction, pData);
// Wait for the task to finish.
pData->signal.wait();
// Free memory allocation.
HeapFree(GetProcessHeap(), 0, pData);
return 0;
}
void __cdecl MyThreadFunction(LPVOID lpParam)
{
PMYDATA pData = (PMYDATA)lpParam;
// Use thread-safe functions to print the parameter values.
TCHAR msgBuf[BUF_SIZE];
StringCchPrintf(msgBuf, BUF_SIZE, TEXT("Parameters = %d, %d\n"),
pData->val1, pData->val2);
size_t cchStringSize;
StringCchLength(msgBuf, BUF_SIZE, &cchStringSize);
DWORD dwChars;
WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), msgBuf, (DWORD)cchStringSize, &dwChars, NULL);
pData->signal.set();
}