web-dev-qa-db-ja.com

C ++で関数を非同期にするにはどうすればよいですか?

非同期になる関数を呼び出したいのですが(このタスクが完了したらコールバックを行います)。

これをシングルスレッドで実行したい。

20
yogesh

これは、最新のC++、または古いC++と一部の boost を使用して移植できます。 boostとC++ 11の両方には、スレッドから非同期値を取得する高度な機能が含まれていますが、コールバックが必要な場合は、スレッドを起動してそれを呼び出すだけです。

1998 C++ /ブーストアプローチ:

#include <iostream>
#include <string>
#include <boost/thread.hpp>
void callback(const std::string& data)
{
    std::cout << "Callback called because: " << data << '\n';
}
void task(int time)
{
    boost::this_thread::sleep(boost::posix_time::seconds(time));
    callback("async task done");
}
int main()
{
    boost::thread bt(task, 1);
    std::cout << "async task launched\n";
    boost::this_thread::sleep(boost::posix_time::seconds(5));
    std::cout << "main done\n";
    bt.join();
}

2011 C++アプローチ(これを必要とするgcc 4.5.2を使用)

#define _GLIBCXX_USE_NANOSLEEP
#include <iostream>
#include <string>
#include <thread>
void callback(const std::string& data)
{
    std::cout << "Callback called because: " << data << '\n';
}
void task(int time)
{
    std::this_thread::sleep_for(std::chrono::seconds(time));
    callback("async task done");
}
int main()
{
    std::thread bt(task, 1);
    std::cout << "async task launched\n";
    std::this_thread::sleep_for(std::chrono::seconds(5));
    std::cout << "main done\n";
    bt.join();
}
15
Cubbi

プレーンなC++ではできません。 OS固有のメカニズムを使用する必要があり、OSがコールバックを実行できるように実行が一時停止されるポイントが必要です。例えば。 Windowsの場合、QueueUserAPC-コールバックは、たとえばSleepExまたはWaitForSingleObjectEx

5
Erik

長い答えは、独自のタスクスケジューラを実装し、「関数」を1つ以上のタスクにまとめることです。あなたが長い答えを望んでいるかはわかりません。それは確かにあなたが何かを呼び出し、それを完全に忘れ、そしてそのことが行われたときに通知されることを許可しません。ただし、意欲的である場合は、標準のC++の外側に到達することなく、コルーチンをある程度のレベルでシミュレートできます。

簡単に言えば、これは不可能です。複数のスレッドまたは複数のプロセスを使用します。開発中のOS /プラットフォームを明かした場合は、より具体的な情報を提供できます。

3
Ken Rockot

これには2つのビットがあります。

最初に、後で実行できるように関数呼び出しをパックします。

次に、それをスケジュールします。

実装の他の側面に依存するのはスケジューリングです。 「このタスクがいつ完了するか」がわかっている場合は、それで十分です。戻って「関数呼び出し」を取得して呼び出すだけです。したがって、これが必ずしも大きな問題であるかどうかはわかりません。

最初の部分は、実際には関数オブジェクト、または関数ポインターについてです。後者は、Cの従来のコールバックメカニズムです。

FOの場合、次のようになります。

class Callback
{
public:
  virtual void callMe() = 0;
};

あなたはこれから派生し、あなたが特定の問題に適していると思うようにそれを実装します。その場合、非同期イベントキューはlist<>のコールバックにすぎません。

std::list<Callback*> asyncQ; // Or shared_ptr or whatever.
3
Keith

私はあなたが何を望んでいるかを理解しているのかわかりませんが、それがコールバックを利用する方法である場合:これは、次のように関数ポインタを定義することで機能します(テストされていません):

// Define callback signature.
typedef void (*DoneCallback) (int reason, char *explanation);

// A method that takes a callback as argument.
void doSomeWorkWithCallback(DoneCallback done)
{
    ...
    if (done) {
       done(1, "Finished");
    }   
}

//////

// A callback
void myCallback(int reason, char *explanation)
{
    printf("Callback called with reason %d: %s", reason, explanation);
}

/////

// Put them together
doSomeWortkWithCallback(myCallback);
1
DarkDust

他の人が言ったように、技術的にはプレーンなC++ではできません。

ただし、タスクを実行し、タイムスライスまたはタイムスケジューリングを行うマネージャーを作成できます。各関数呼び出しで、マネージャーはタイマーを使用して、プロセスにかかった時間を測定します。プロセスが予定よりも短い時間で完了し、別の呼び出しを終了して残りの時間を使い切らずに使い切ることができると判断した場合は、再度呼び出すことができます。関数が割り当てられた時間を超えた場合、関数が次に実行する更新の時間が少なくなることを意味します。したがって、これには、それを処理するためのやや複雑なシステムの作成が含まれます。

または、特定のプラットフォームを念頭に置いている場合は、スレッドを使用したり、作業を処理する別のプロセスを作成したりできます。

0
leetNightshade

C++ 11以降、プレーンなc ++ doesにはスレッドの概念がありますが、関数を非同期的に呼び出す最も簡潔な方法は、C++ 11 asyncコマンドをフューチャーと共に使用することです。これは、pthreadで同じことを行う方法とよく似ていますが、すべてのOSとプラットフォームに100%移植できます。

関数に戻り値があるとしましょう... int = MyFunc(int x、int y)

#include <future>

ただやる:

// This function is called asynchronously
std::future<int> EventualValue = std::async(std::launch::async, MyFunc, x, y); 

キャッチ?それがいつ完了したかをどのようにして知るのですか? (バリア)

最終的に、次のことを行います。

int MyReturnValue = EventualValue.get(); // block until MyFunc is done

この方法で並列forループを実行するのは簡単です。先物配列を作成するだけです。

0
user2465201