にゃははー

はへらー

std::futureで未来に生きる

非同期にスレッドを走らせたい時、そしてその戻り値を取りたい時にstd::futureを使うといいです。

なお、4.7.0 20110510(experimental)時点でlibstdc++のthreadはデフォルトでstd::this_threadの関数が使えません。先頭の2つのdefineはそれらを有効化するためのものです。
4.7.0ではconcurrencyが改善されるといいですねthread_localとかthread_localとか。

#define _GLIBCXX_USE_NANOSLEEP
#define _GLIBCXX_USE_SCHED_YIELD
#include <iostream>
#include <chrono>
#include <thread>
#include <future>
using namespace std;
using namespace std::chrono;

int main()
{
	auto begin = system_clock::now();
	auto f = async( []() -> int
	{
		this_thread::sleep_for( seconds( 1 ) );
		return 1;
	} );

	cout << "pre-condition: " << boolalpha << f.valid() << endl;
	this_thread::yield();
	cout << "value: " << f.get() << endl;
	cout << "post-condition: " << boolalpha << f.valid() << endl;
	cout << duration_cast< milliseconds >( system_clock::now() - begin ).count() << " ms" << endl;
}

std::futureの例です。std::asyncにfunctorとそれの引数をずらずらーっと。
std::futureはmove ctorを持ってるのでそのままautoに束縛できます。

std::future::validは有効な値が代入されているか→この場合asyncに渡したfunctorが終了したか がわかります。
pre-conditionが表示され、1秒ほどたってからvalueとpost-conditionが表示されます。

ここで注意ですが、coutをoperator<<でずらずら全部つなげると私みたいにバカを見ますので注意してください。

仕様で一度get()してしまうとvalid()はfalseを返すようになりますので注意してください。(その場合はstd::shared_futureかな?

次はstd::promise
こちらはスレッド間通信を行うのにいいクラスです。

#define _GLIBCXX_USE_NANOSLEEP
#define _GLIBCXX_USE_SCHED_YIELD
#include <iostream>
#include <utility>
#include <chrono>
#include <thread>
#include <future>
using namespace std;
using namespace std::chrono;

int main()
{
	auto begin = system_clock::now();

	promise< int > p;
	auto ap = p.get_future();
	thread another_world( []( promise< int > &p )
	{
		this_thread::sleep_for( seconds( 1 ) );
		p.set_value( 1 );
	}, move( p ) );

	cout << "pre-condition: " << boolalpha << ap.valid() << endl;
	this_thread::yield();
	cout << "value: " << ap.get() << endl;
	cout << "post-condition: " << boolalpha << ap.valid() << endl;
	cout << duration_cast< milliseconds >( system_clock::now() - begin ).count() << " ms" << endl;

	another_world.join();
}

先のstd::futureみたいな例ですが、こちらは戻り値ではなく任意のタイミングでstd::futureに対して値を設定できます。ただし、std::futureに直接入れるのではなくstd::promiseを介して渡します。

変数名がアレなのは先のついーとを意識した感じです。std::refとかではなくstd::moveを使ってるのも別空間に飛んていってください的な感じで。std::promiseもmove ctorを持っているので問題ないですね。

今回はstd::threadなのでjoin()するのを忘れない様に。detatch()したスレッドとも通信できるのでいいですね。
仕様を理解してない故の懸念ですが、detachしたスレッドより早くmainが終了した場合どうなるんですかね。まぁそれはまたべつのお話ということで。

==== 5/10 20:30 追記 ====
std::move()でなくstd::ref()を使って渡した場合、std::promise::set_*()を呼ばずに終了してもstd::future::get()で検出することができません。
std::move()を使って渡した場合はstd::promise::set_*()を呼ばずに終了した場合std::futureとstd::promise間のpipeが切れ、std::future::get()でstd::future_errorが投げられます。
私の勘はあたってたのですねー moveしましょう!