Using C++11 Lambda functions with the C++Builder Parallel Programming Library

by Apr 7, 2016

With the introduction of the Parallel Programming Library (PPL) and Embarcadero C++Builder support for C++11 lambda functions on the Win32, Win64, iOS and Android platforms, you can now use the two in combination in your applications. Below you will find C++Builder example source code that uses lambda functions with the Parallel For, Tasks and Futures. If you use C++11 features (including lamda functions) in your applications that target Win32, make sure you turn off (set to False) the "Use classic Borland compiler" in the Projects | Options | C++ Compiler dialog.

Parallel For

The Parallel Programming Library (PPL) includes a Parallel for loop method. The TParallel.For accepts anonymous methods in Delphi whereas in C++ you create an Iterator event function or C++11 lambda and pass that as part of the TParallel::For loop call. In this example, I show you the two ways to use an iterator function in the Parallel::For method: using an event style iterator and using a C++11 lambda. I've commented out the use of the iterator function in favor of using the C++11 lambda function. Note: the variable Tot is defined in the public section of the form class. 

#include <System.Threading.hpp>
#include <System.Diagnostics.hpp>
#include <System.SysUtils.hpp>

...

// test if a number is a prime number
bool IsPrime(int N) {
  bool aPrime = true;
  for (int Test = 2;Test<=N-1;Test++) {
	if (N % Test == 0) {
	  aPrime = false;
	  break; //jump out of the for loop
	}
  }
  return aPrime;
}

// Parallel For Iterator Event Proc
void __fastcall TForm2::MyIteratorEvent(TObject* Sender, int AIndex)
{
	if (IsPrime(AIndex)) {
		TInterlocked::Increment(Tot);
	};
}

// Button event handler that finds prime numbers using Parallel::For
void __fastcall TForm2::Button2Click(TObject *Sender)
{
  // counts the prime numbers below a given value
  int Max = 50000; // 50K
  Tot = 0;
  System::Diagnostics::TStopwatch sw = System::Diagnostics::TStopwatch::Create();
  sw.Start();
  // TParallel::For(NULL,1,Max,MyIteratorEvent);  // using an iterator "event" method
  // using C++11 Lambda for the parallel for (Note: for Win32 turn off option "Use Classic Compiler"
  TParallel::For(NULL,1,Max,
	  System::Sysutils::_di_TProc__1<int>(
		  // [this] (int AIndex) {
		  [&] (int AIndex) {
			  if (IsPrime(AIndex)) {
				  TInterlocked::Increment(Tot);
			  };
		  }
	  )
  );
  sw.Stop();
  Memo1->Lines->Add (
	String().sprintf(L"Parallel For loop. Time (in milliseconds): %lld, Primes found: %d",
				sw.ElapsedMilliseconds,Tot)
  );
}

// Button event handler that uses sequential for look to find prime numbers
void __fastcall TForm2::Button1Click(TObject *Sender)
{
  // counts the prime numbers below a given value
  int Max = 50000; // 50K
  Tot = 0;
  System::Diagnostics::TStopwatch sw = System::Diagnostics::TStopwatch::Create();
  sw.Start();
  for (int I = 1;I<=Max;I++) {
	if (IsPrime(I)) {
	  Tot++;
	  // Application.ProcessMessages;
	}
  }
  sw.Stop();
  Memo1->Lines->Add (
	String().sprintf(L"Sequential For loop. Time (in milliseconds): %lld, Primes found: %d",
				sw.ElapsedMilliseconds,Tot)
  );
}

 

Parallel Task

The Parallel Programming Library (PPL) provides a TTask class to run one task or multiple tasks in parallel. A Task is a unit of work you need to get done. The PPL does the association between the task and the thread that performs the task so you can run several tasks in parallel without having to create your own custom threads and managing them. This example creates two parallel tasks that use C++11 lambda functions. Inside of the button event handler, the Parallel Task WaitForAny and WaitForAll methods are used.

#include <System.Threading.hpp>

...

void __fastcall TForm1::Button1Click(TObject *Sender)
{
   int counter = 0;
   _di_ITask tasks[2];

   tasks[0] = TTask::Create([&] () {
	   Sleep(3000);
	   TInterlocked::Add(counter,1);

   });

   tasks[1] = TTask::Create([&] () {
	   Sleep(5000);
	   TInterlocked::Add(counter,1);

   });

   for(auto task: tasks) {
	   task->Start();
   }

   Label1->Caption = "All Tasks Started";
   Label1->Update();


   TTask::WaitForAny(tasks,(sizeof(tasks)/sizeof(tasks[0])-1));
   Label1->Caption = "At least one task is done! "+IntToStr(counter);
   Label1->Update();

   TTask::WaitForAll(tasks,(sizeof(tasks)/sizeof(tasks[0])-1));
   Label1->Caption = "All tasks are done! "+IntToStr(counter);
   Label1->Update();
}

 

Parallel Future

This sample shows you how to use C++11 lambda functions with the Parallel Future feature in the Parallel Programming Library. Parallel Future uses TTask::Future to launch a function that returns a specific type. TTask::Future<T>, where the type parameter, T, represents the return type. Using a future allows you to calculate something or run some query and at the same time you can do other tasks, getting the value when you want via future's value. If the value is not completed yet, the Parallel Programming Library blocks the current code waiting for this value to be calculated. This allows you to prioritize code blocks to run in the order you want, but still ensure you get the value at the point you need it.

#include <system.threading.hpp>

...

void __fastcall TForm1::Button1Click(TObject *Sender)
{
	auto future = TTask::Future<int>([&] () -> int {
		// very long computation can go here
		Sleep(2000);
		return 42;
	});
	// do other things while the future has not been given a value
	Sleep(1000);
	// the thread will block until the future value is computed/set
	int computedValue = future->Value;
	ShowMessage("Future has a value: "+IntToStr(computedValue));

}

 

Links to Additional Resources

 

Source Code on Code Central

You can download the source code examples (tested using C++Builder 10 Seattle) for all three projects using the CodeCentral link – "Using C++11 Lambda functions with the C++Builder PPL" – http://cc.embarcadero.com/item/30508.