C++同時拡張インタフェースstd::asyncとstd::future

26188 ワード

std::asyncは呼び出し可能なオブジェクトを独立したスレッドで実行できます.std::futureはスレッドの終了を待って結果を取得できます.次のコードでは、asyncは関数をすぐに非同期で分離したスレッドに起動し、futureオブジェクトに戻って関数結果または異常を取得できるようにします.
futureオブジェクトのgetメソッドが呼び出されると、次の3つのことが起こります.
  • 関数がasyncによって分離スレッドで開始され、終了すると、すぐに結果が得られます.
  • 関数がasyncによって分離スレッドで開始されたがまだ終了していない場合、getは関数の終了後に結果を得るのを待つ.
  • 関数がまだ起動していない場合、同期呼び出しのように強制的に起動され、getは関数が終了した後に結果を得るのを待つ.
  • #include 
    #include 
    #include 
    
    int printTag(const string& tag)
    {
    	int ret = 0;
    	for (int i = 0; i < 10; ++i) {
    		this_thread::sleep_for(chrono::milliseconds(100));
    		cout << tag;
    		ret += i;
    	}
    	return ret;
    }
    
    int main()
    {
        //babababaababbababaab90
    	future<int> result1(async([] {return printTag("a"); }));
    	int result2 = printTag("b");
    	cout << result1.get() + result2 << endl;
    	system("pause");
    }
    

    asyncはlaunchポリシーasyncを使用して、ターゲット関数を非同期で起動することを明確にする必要があります.非同期呼び出しがここで実現できない場合(例えば、現在の環境でマルチスレッドがサポートされていない場合、または新しいスレッドが作成できない場合)、プログラムはstd::system_error異常.
    future<int> result1(async(launch::async, [] {return printTag("a"); }));
    

    launchポリシーdeferredを使用すると、futureオブジェクトにgetまたはwaitが呼び出されるまで、ターゲット関数の実行を強制的に遅らせることができます.
    int main()
    {
        //bbbbbbbbbbaaaaaaaaaa90
    	future<int> result1(async(launch::deferred,[] {return printTag("a"); }));
    	int result2 = printTag("b");
    	cout << result1.get() + result2 << endl;
    	system("pause");
    }
    

    asyncの結果を割り当てない場合(deferredポリシーを除く)、呼び出し元はターゲット関数が終了するまで待機します.
    int main()
    {
    	//aaaaaaaaaabbbbbbbbbb
    	async([] {return printTag("a"); });
    	int result2 = printTag("b");
    	system("pause");
    }
    

    スレッドで処理されていない例外はfutureオブジェクトにスナップされ、getが呼び出されたときに再び放出されます.
    void throwExceptionFunc()
    {
    	try {
    		throw exception();
    	}
    	catch (...) {
    		cout << "catch exception in sub thread" << endl;
    	}
    }
    
    int main()
    {
    	future<void> result1(async(throwExceptionFunc));
    	try {
    		result1.get();
    	}catch (...) {
    		cout << "catch exception in main thread" << endl;
    	}
    	system("pause");
    }
    

    1つのfutureオブジェクトはgetメソッドを1回しか呼び出せず、その後futureオブジェクトは無効な状態にあり、validメソッドで検出できます.
    futureはまた、結果を処理する必要がなく、バックグラウンド操作の完了を待つためのインタフェースのセットを提供します.これらのインタフェースは、wait、wait_で1回以上呼び出すことができます.for(しばらく待って)、wait_until(特定の時点が到着するまで待機)では、スレッド内の未処理の例外は放出されません.
    wait_for,wait_untilは、次の3つの値を返します.
  • future_status::deferred:asyncはdeferredポリシーを使用し、waitメソッドやgetメソッドを呼び出したことがなく、この場合すぐに戻ります.
  • future_status::timeout:操作は非同期で開始されたがまだ終了せず、待機時間が到来した.
  • future_status::ready:操作は正常に完了しました.
  • void printFunc()
    {
    	while (true) {
    		this_thread::sleep_for(chrono::seconds(1));
    		cout << "runing..." << endl;
    	}
    }
    
    int main()
    {
    	
    	future<void> result1(async(printFunc));
    	cout << (int)result1.wait_for(chrono::seconds(10))<< endl;
    	cout << "wait finished!"<<endl;
    	system("pause");
    }
    

    Lambdaを使用する場合は、値キャプチャまたは参照キャプチャを使用して実パラメータを関数に渡すか、asyncを使用して実パラメータを直接渡す(値キャプチャと同等)ことができます.
    void modifyStr(string& str)
    {
    	str+= "_modifyed";
    }
    
    void printStr(const string& str)
    {
    	cout << str << endl;
    }
    
    int main()
    {
     
    	string testStr = "hello";
    	auto result1=async(printStr, testStr);//  async    
    	result1.wait();
    
    	auto result2 = async([=]()mutable{modifyStr(testStr); });//   
    	result2.wait();
    	cout << testStr << endl;//hello
    
    	auto result3 = async([&] {modifyStr(testStr); });//    
    	result3.wait();
    	cout << testStr << endl;//hello_modifyed
    	system("pause");
    }
    

    futureはgetメソッドを1回しか呼び出せませんがshared_futureはgetメソッドを複数回呼び出すことができ(同じ結果を返す)、複数のスレッドが同じ結果を処理したい場合に便利です.
    int queryNumber()
    {
    	cout << "read number:";
    	int num;
    	cin >> num;
    	if (!cin) {
    		return 0;
    	}
    	return num;
    }
     
    
    void printChar(char c, shared_future<int> f)
    {
    	int num = f.get();
    	for (int i = 0; i < num; i++) {
    		this_thread::sleep_for(chrono::milliseconds(100));
    		cout << c;
    	}
    }
    
    int main()
    {
     
    	shared_future<int> f = async(queryNumber);
    	auto f1 = async(launch::async, printChar, 'A', f);
    	auto f2 = async(launch::async, printChar, 'B', f);
    	auto f3 = async(launch::async, printChar, 'C', f);
    	f1.wait();
    	f2.wait();
    	f3.wait();
    	system("pause");
    }