nlib
misc/threading/future/future.cpp

Futureのサンプルです。 Future / Promiseを利用すると、非同期的に処理を実行する場合の同期や計算結果の受け渡しを簡単かつ安全に行うことができます。

PackagedTask, Asyncは、FuturePromiseを利用して実装されていて、処理をより簡潔に記述することができます。

using ::nlib_ns::threading::Future;
using ::nlib_ns::threading::Promise;
using ::nlib_ns::threading::PackagedTask;
using ::nlib_ns::threading::Thread;
using ::nlib_ns::threading::ThreadSettings;
using ::nlib_ns::threading::ThreadArg;
using ::nlib_ns::UniquePtr;
static int DeferredPlus(int x, int y) {
return x + y;
}
static int Square(Future<int>& x) { // NOLINT
// Future::Get() will not block if
int tmp = x.Get();
return tmp * tmp;
}
bool UsePackagedTask() {
// Use 'PackagedTask<R(...)>' and 'GetFuture()' function to retrieve 'Future<R>'.
// the template arguments for PackagedTask are the return type and the argument types.
PackagedTask<int(int, int)> task; // NOLINT
if (task.Init(DeferredPlus) != 0) return false;
Future<int> future;
// you can get a Future object from PackagedTask.
// The result of PackagedTask on the other thead is retrieved safely by it.
if (task.GetFuture(&future) != 0) {
return false;
} else {
Thread th;
ThreadSettings settings;
settings.SetDetachState(true);
int val1 = 1;
int val2 = 2;
if (th.Start(settings, task, val1, val2, nlib_ns::move_tag()) != 0) return false;
}
nlib_printf("Start PackagedTask{ sleep(2000msec); return x(1) + y(2); }\n");
// Future::Get() waits until the result is available.
// you can use Future::WaitFor(), Future::WaitUnitl() for timeout.
int result = future.Get();
nlib_printf("Result of PackagedTask = %d\n", result);
return true;
}
bool UseAsync() {
// you can also use Async instead of PackagedTask.
// A thread is invoked inside Async, and it is detached inside Async.
Future<int> future;
nlib_printf("Start Async{ sleep(2000msec); return x(2) + y(3); }\n");
if (Async(&future, DeferredPlus, 2, 3) != 0) return false;
int result = future.Get();
nlib_printf("Result of Async = %d\n", result);
return true;
}
void CreateData(UniquePtr<ThreadArg<Promise<UniquePtr<int[]> > > >& ptr) { // NOLINT
// CreateData() runs concurrently with UsePromiseAndFuture().
// You can package I/O tasks by Promise/Future.
" Another thread allocates memory and sets data,\n"
" then returns the memory to the main thread.\n");
UniquePtr<int[]> data(new int[1024]);
for (int i = 0; i < 1024; ++i) {
data[i] = i;
}
// You can avoid memory leaks if the main thread gives up the I/O.
errno_t e = ptr->arg1.SetValue(data.release());
NLIB_UNUSED(e);
NLIB_ASSERT_NOERR(e);
}
bool UsePromiseAndFuture() {
ThreadArg<Promise<UniquePtr<int[]> > >::ArgType p(new ThreadArg<Promise<UniquePtr<int[]> > >());
Future<UniquePtr<int[]> > myfuture;
p->arg1.GetFuture(&myfuture);
p->func = CreateData;
nlib_printf("Start Promise/Future\n");
{
Thread th;
ThreadSettings settings;
settings.SetDetachState(true);
if (th.StartRaw(settings, p) != 0) return false;
}
// you can use Future::Release() to release the ownership of the object.
int* ptr = myfuture.Get();
NLIB_ASSERT(ptr);
nlib_printf("End\n");
// verify the data
for (int i = 0; i < 1024; ++i) {
if (ptr[i] != i) return false;
}
return true;
}
bool UseContinuation() {
// Read the article below about continuation
// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3558.pdf
nlib_printf("Start UseContinuation\n");
Future<int> future1;
if (Async(&future1, DeferredPlus, 2, 3) != 0) return false;
// Square() will start after 'future1' becomes valid (after DefferedPlus returns).
// Square() takes the future of DefferedPlus as the argument.
Future<int> future2;
if (future1.Then(&future2, Square) != 0) return false;
errno_t e = future1.Wait();
if (e != 0) return false;
int result = future2.Get();
nlib_printf("x = DefferedPlus(2, 3), Square(x, x) = %d, %d\n", future1.Get(), result);
return true;
}
static bool SampleMain(int, char**) {
return UsePackagedTask() && UseAsync() && UsePromiseAndFuture() && UseContinuation();
}
NLIB_MAINFUNC