2017-02-22 2 views
3

私は先物のリストを持っています。問題は、私はたくさんのファイルを持っているので、すべてのファイルを作成した後に長い操作をする必要があるということです。それで、私はそれぞれの "ファイル保存"の後にコールバックを作りたいのです。コールバックを使用した非同期の未来。 C++ 11

例えば、

(new thread; saveFile 1.txt -> new thread; do a long operation after the file has been created) 
    (new thread; saveFile 2.pdf -> new thread; do a long operation after the file has been created). 

は、私は別のスレッドですべてを行う必要があります。ファイルの保存は重要です。ファイルが作成される前に2番目のタスクを実行することはできません。 どうすればいいですか? は、私は、次のコードを持っている:

void save_file() { 
    // preparing data... 
    saving a file 
    } 

    std::vector<std::future<void>> saveFileTasks; 
    for (int n = 0; n < p.size(); ++n) 
    { 
     saveFileTasks.push_back(std::async(std::bind(&saveFile, filename))); 
    } 

    for (auto &e : saveFileTasks) { 
     e.get(); 
    } 

は、どのように私はC++ 11 /将来の約束でコールバックを作ることができますか?私は私のプロジェクトでブーストを使用することは許されていません。

私は本当に混乱しています。非常に複雑な例が非常に単純な作業のためにあります。多くの例をコンパイルすることはできません。たとえば、promise.set_wait_callbackはC++ 11には存在しませんが、多くの関数はC++ 11に移行されています。 私はPythonやClojureを使うととても簡単にやります。 C++でどうすればいいですか?

+0

'std :: future :: wait'は仕事をしませんか?とにかくファイルが保存されるまで待たなければならない場合、 'save_file'と' long_operation'を別々のスレッドで実行する点は何ですか? – doc

+0

私はある種のスレッドプールが必要です。私は多くの仕事をすることができます。たとえば、1つは10個のファイルを保存する必要があります。 5つのファイルが保存されると、5つのスレッドで長い操作を実行でき、他の5つのスレッドは他のファイルを保存します。 – user565447

答えて

4

残念ながら、現在のバージョンstd::futureには、.thenの継続がありません。これは、C++の将来の標準のための同様のユーティリティと一緒に提案されています。

あなたがboostを使用できない場合、あなたは関数合成を使用して独自の継続を構築することができます:std::async([data]{ do_operation(save_file(data)); }は同じスレッドで両方の機能を実行することを

string save_file(string data)  { /* ... */ return filename; } // step 1 
void do_operation(string filename) { /* ... */ }     // step 2 

// ... 

std::vector<std::future<void>> fileTasks; 
for(const auto& data : /* ... */) 
{ 
    fileTasks.emplace_back(std::async(std::launch::async, 
     [data]{ do_operation(save_file(data)); }); 
} 

は注意。あなたは、各機能を個別のスレッドで実行したい場合は、呼び出すことができますasync複数回:boost::futureまたは標準の将来のバージョンのいずれかで

std::async(std::launch::async, [data] 
{ 
    auto result = save_file(data); 
    std::async(std::launch::async, [r = std::move(result)] 
    { 
     do_operation(std::move(r)); 
    }); 
}); 

は、あなたが単に言うことができる:

std::async(std::launch::async, [data]{ save_file(data); }) 
    .then([](string filename){ do_operation(filename);); 
+0

私は「class std :: future にはemplace_back属性がありません」というエラーがあります。説明してください、その方法は何ですか? – user565447

+1

@ user565447 'fileTasks'は' std :: future'ではなく 'std :: vector'です。ミスタイプのものが必要です。 – Angew

+0

ありがとう、私はそれが動作するかどうかを確認しています。 – user565447

4

将来的には、作業を連鎖させる.then演算子を使用します。

私たちはそれを書くことができません。

// complete named operator library in about a dozen lines of code: 
namespace named_operator { 
    template<class D>struct make_operator{ constexpr make_operator() {}; }; 

    template<class T, class O> struct half_apply { T&& lhs; }; 

    template<class Lhs, class Op> 
    half_apply<Lhs, Op> operator*(Lhs&& lhs, make_operator<Op>) { 
    return {std::forward<Lhs>(lhs)}; 
    } 
    template<class Lhs, class Op, class Rhs> 
    decltype(auto) operator*(half_apply<Lhs, Op>&& lhs, Rhs&& rhs) 
    { 
    return named_invoke(std::forward<Lhs>(lhs.lhs), Op{}, std::forward<Rhs>(rhs)); 
    } 
} 

// create a named operator then: 
namespace then_ns { 
    static const struct then_t:named_operator::make_operator<then_t> {} then{}; 

    namespace details { 
    template<size_t...Is, class Tup, class F> 
    auto invoke_helper(std::index_sequence<Is...>, Tup&& tup, F&& f) 
    ->decltype(std::forward<F>(f)(std::get<Is>(std::forward<Tup>(tup))...)) 
    { 
     return std::forward<F>(f)(std::get<Is>(std::forward<Tup>(tup))...); 
    } 
    } 

    // first overload of A *then* B handles tuple and tuple-like return values: 
    template<class Tup, class F> 
    auto named_invoke(Tup&& tup, then_t, F&& f) 
    -> decltype(details::invoke_helper(std::make_index_sequence< std::tuple_size<std::decay_t<Tup>>{} >{}, std::forward<Tup>(tup), std::forward<F>(f))) 
    { 
    return details::invoke_helper(std::make_index_sequence< std::tuple_size<std::decay_t<Tup>>{} >{}, std::forward<Tup>(tup), std::forward<F>(f)); 
    } 

    // second overload of A *then* B 
    // only applies if above does not: 
    template<class T, class F> 
    auto named_invoke(T&& t, then_t, F&& f, ...) 
    -> std::result_of_t< F(T) > 
    { 
    return std::forward<F>(f)(std::forward<T>(t)); 
    } 
    // *then* with a future; unpack the future 
    // into a call to f within an async: 
    template<class X, class F> 
    auto named_invoke(std::future<X> x, then_t, F&& f) 
    -> std::future< std::decay_t<decltype(std::move(x).get() *then* std::declval<F>())> > 
    { 
    return std::async(std::launch::async, 
     [x = std::move(x), f = std::forward<F>(f)]() mutable { 
     return std::move(x).get() *then* std::move(f); 
     } 
    ); 
    } 
    // void future, don't try to pass void to f: 
    template<class F> 
    auto named_invoke(std::future<void> x, then_t, F&& f) 
    -> std::future< std::decay_t<decltype(std::declval<F>()())> > 
    { 
    return std::async(std::launch::async, 
     [x = std::move(x), f = std::forward<F>(f)]() mutable { 
     std::move(x).get(); 
     return std::move(f)(); 
     } 
    ); 
    } 
} 
using then_ns::then; 

はそれほど難しくはなかったことを、ご覧ください。

a *then* faがタプル(またはペアまたは配列)の場合、aの内容でfが呼び出されます。

aの場合はタプルに似ていない、またはfaの内容をそのように受け入れていない、それはafを呼び出します。

aた場合は、それが代わりに*then*を使用してa.get()を消費し、新しい非同期未来を作成し、将来です。

Live example

は、ファイルを保存するときに、原子INTを増やしたいとします

std::vector<std::future<void>> saveFileTasks; 
for (int n = 0; n < p.size(); ++n) 
{ 
    saveFileTasks.push_back(
    std::async(std::launch::async, [filename]{ 
     saveFile(filename); 
    }) 
); 
} 
std::atomic<int> count; 
for (auto &e : saveFileTasks) { 
    e = std::move(e) *then* [&count]{ 
    ++count; 
    }); 
} 

当然のことながら、これは、すべての名前付きオペレータ*then*スタイルの構文なしで行うことができますが、それの楽しみは何ですか?

最初のasyncがタプルを返した場合、2番目のタプルはタプルとして、または解凍された "フラット"引数として取り込むことができます。

+3

'* then *'は恐ろしく美しいです。私はUFCSを夢見ています... –

+1

あなたのコードを掘り下げなければならない人にとってはそれほど楽しいものではありません。 – doc

+0

@VittorioRomeoクラスに組み込み型を宣伝したり、クラスオブジェクトを提供したり(Rubyのようなもの、Javascriptのようなもの)この方法で、クラスオブジェクトにメソッドを追加することができます。これにより、メソッドのクラスの保護されたメンバーにアクセスすることもできます。 – doc

関連する問題