Edit

Share via


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

  1. Add a #include directive for the header file concrt.h.
#include <concrt.h>
  1. Add a using directive for the concurrency namespace.
using namespace concurrency;
  1. Change the declaration of MyThreadFunction to use the __cdecl calling convention and to return void.
void __cdecl MyThreadFunction(LPVOID param);
  1. 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;
  1. Replace the call to CreateThread with a call to the concurrency::CurrentScheduler::ScheduleTask method.
CurrentScheduler::ScheduleTask(MyThreadFunction, pData);
  1. 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();
  1. Remove the call to CloseHandle.

  2. Change the signature of the definition of MyThreadFunction to match step 3.

void __cdecl MyThreadFunction(LPVOID lpParam)
  1. 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();
  1. Remove the return statement from MyThreadFunction.

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();
}

See also

Task Scheduler
Scheduler Class