C++11 Praktische Technologie (3) std::future, std::promise, std::packaged_task, async

Katalog der C++ Advanced Series

Verwendung von C++-Operatorschlüsselwörtern (überladene Operatoren, Funktoren, Typkonvertierungsoperatoren)

C++ 11 praktische Technologie (1) die Verwendung von Auto und DeclType

Praktische C++11-Technologie (2) std::function und bind binder

C++11 Praktische Technologie (3) std::future, std::promise, std::packaged_task, async

Einführung

Die Multithread-Anweisung von C++ ist Thread. Zum Beispiel

#include <iostream>
#include <thread>

// 线程函数,打印一条消息
void printMessage(const std::string& message) {
    
    
    std::cout << "Message: " << message << std::endl;
}
int main() {
    
    
    // 创建线程对象,并指定线程函数及其参数
    std::thread t(printMessage, "Hello, world!");
    // 等待线程执行完毕
    t.join();
    return 0;
}

Wir können mehrere Threads über Thread erstellen. Und die Ausführungsaufgabenfunktion wird in Form einer Rückruffunktion zur Ausführung an den Multithread übergeben. Aber es gibt ein Problem mit dem Thread:

Wir haben keine Schnittstelle zum Abrufen von Aufgabenfunktionen, das heißt, es ist für uns schwierig , den Rückgabewert oder internen Parameterwert unserer Aufgabenfunktion im Hauptthread zu erhalten . (Es kann über globale Variablen abgerufen werden, es besteht jedoch ein gewisses Risiko der Parallelität und es ist nicht elegant.)

Und std::future und std::promise können solche Szenarien bewältigen.

std::future&std::async

Wenn Sie das std::future-Objekt im Thread erhalten möchten, müssen Sie es in Kombination mit std::promise verwenden, was unten erwähnt wird.
C++11 kann std::async verwenden, um Aufgaben bequemer asynchron zu erstellen und auszuführen, d. h. Multithread-Ausführungsaufgaben (gleiche Funktion wie Thread). Und das von async zurückgegebene Ergebnis kann von std::future empfangen werden.
Zum Beispiel:

#include <iostream>
#include <future>
int calculateSum(int a, int b) {
    
    
    return a + b;
}
int main() {
    
    
    std::future<int> result = std::async(calculateSum, 2, 3);
    // 执行其他操作...
    // 获取异步任务的结果
    int sum = result.get();
    // 输出结果
    std::cout << "Sum: " << sum << std::endl;
    return 0;
}

Es ist ersichtlich, dass die von unserem Async erstellte asynchrone Aufgabe von std::future empfangen werden kann und der Rückgabewert durch Verwendung von .get() im Hauptthread abgerufen werden kann.
Hinweis:
result.get() ist hier blockiert und erhält den Rückgabewert erst, wenn die Aufgabenausführung abgeschlossen ist. (Async wird nach Erhalt des Rückgabewerts wiederverwendet, im Gegensatz zu Threads, die auch join() verwenden müssen).


Einschließlich .get() gibt es drei Möglichkeiten, zukünftige Ergebnisse in verschiedenen Szenarien zu erhalten (bei Interesse können Sie mehr erfahren):

  • get: Blockieren wartet auf das Ende des asynchronen Vorgangs und gibt das Ergebnis zurück.
  • Warten: Warten Sie einfach, bis der asynchrone Vorgang abgeschlossen ist, kein Rückgabewert.
  • wait_for: Durch Polling wird regelmäßig der zukünftige Status abgefragt, in dem Wissen, dass die Aufgabe abgeschlossen ist.

std::async verfügt außerdem über zwei Strategien zur Thread-Erstellung:

  • std::launch::async: Beginnen Sie mit der Erstellung von Threads, wenn Sie async aufrufen, was die Standardeinstellung ist.
  • std::launch::deferred: Durch verzögertes Laden werden Threads erstellt. Wenn Async aufgerufen wird, wird kein Thread erstellt. Ein Thread wird nur erstellt, wenn Get oder Wait von Future aufgerufen werden.

Beispielverwendung:

std::future<int> result = std::async(std::launch::async, calculateSum, 2, 3);
std::future<int> result = std::async(std::launch::deferred, calculateSum, 2, 3);

Hinweis für die Zukunft:
Verwenden Sie .get(), um den Rückgabewert der Aufgabe abzurufen, und Sie können ihn nur einmal abrufen. Bei mehrmaligem Zugriff wird ein Fehler gemeldet.

#include <iostream>
#include <future>
int calculateSum(int a, int b) {
    
    
	return a + b;
}
int main() {
    
    
	std::future<int> result = std::async(calculateSum, 2, 3);
	// 执行其他操作...
	// 获取异步任务的结果
	int sum = result.get();
	// 输出结果
	std::cout << "Sum: " << sum << std::endl;

	int sum1 = result.get();//这里第二次使用get会报错
	return 0;
}

Das obige int sum = result.get(); und int sum1 = result.get(); melden bei der zweiten Verwendung einen Fehler.
Fügen Sie hier eine Bildbeschreibung ein

Wenn Sie das Rückgabeergebnis der asynchronen Aufgabe mehrmals erhalten möchten, können Sie std::shared_future;

shared_future

Future kann das shared_future-Objekt über .share() abrufen.

#include <iostream>
#include <future>

// 异步任务函数,返回一个结果
int asyncTask() {
    
    
	// 模拟耗时操作
	std::this_thread::sleep_for(std::chrono::seconds(5));
	return 42;
}

int main() {
    
    
	// 创建异步任务,并获取 std::future 对象
	std::future<int> fut = std::async(std::launch::async, asyncTask);

	// 创建共享的 std::shared_future 对象
	std::shared_future<int> sharedFut = fut.share();

	// 在多个线程中获取共享的异步任务结果
	std::thread t1([&sharedFut]() {
    
    
		int result = sharedFut.get();
		std::cout << "Thread 1 Result: " << result << std::endl;
		});

	std::thread t2([&sharedFut]() {
    
    
		int result = sharedFut.get();
		std::cout << "Thread 2 Result: " << result << std::endl;
		});

	t1.join();
	t2.join();

	return 0;
}

Das Obige verwendet fut, um ein gemeinsam genutztes std::shared_future-Objekt sharedFut zu erstellen und es an die beiden Threads zu übergeben. Diese beiden Threads rufen jeweils sharedFut.get() auf, um das Ergebnis der gemeinsam genutzten asynchronen Aufgabe abzurufen.

Beachten Sie, dass std::shared_future in mehreren Threads gleichzeitig auf das Ergebnis zugreifen kann, ohne das Warten zu blockieren. Dadurch können mehrere Threads gleichzeitig Ergebnisse asynchroner Aufgaben abrufen.

std::promise

Die obige Zukunft kann das von unserer asynchronen Aufgabe zurückgegebene Ergebnis erhalten, und das Versprechen kann den Zwischenwert unserer asynchronen Aufgabe erhalten . Wird verwendet, um einen Wert in einem Thread zu erzeugen und diesen Wert in einem anderen Thread abzurufen. Sie können damit einen Wert in einem Thread festlegen und ihn mit einem std::future verknüpfen, um diesen Wert in einem anderen Thread abzurufen.
Beispielsweise erhält der Hauptthread den Summenwert der lokalen Funktion in der asynchronen Thread-Task-Task:

#include <iostream>
#include <future>

void calculateSum(std::promise<int>& promise, int a, int b) {
    
    
    int sum = a + b;
    // 设置值
    promise.set_value(sum);
}
int main() {
    
    
    std::promise<int> promise;
    std::future<int> result = promise.get_future();
    // 启动异步任务
    std::thread thread(calculateSum, std::ref(promise), 2, 3);//这里用std::async也是一样的,但这里我们不需要获取task的返回值,所以就使用thread。
    // 执行其他操作...
    // 获取异步任务的结果
    int sum = result.get();
    // 输出结果
    std::cout << "Sum: " << sum << std::endl;
    // 等待线程结束
    thread.join();
    return 0;
}

Das Obige erstellt ein std::promise-Objektversprechen und erhält das damit verbundene std::future-Objektergebnis durch Aufruf von Promise.get_future(). Starten Sie dann einen neuen Thread, übergeben Sie die Funktion „calcureSum“ als Argument und übergeben Sie das Promise-Objekt und zwei ganzzahlige Parameter.

Nachdem wir andere Operationen ausgeführt haben, erhalten wir das Ergebnis der asynchronen Aufgabe, indem wir result.get() aufrufen. Dadurch wird der aktuelle Thread blockiert, bis die Ausführung von Promise.set_value(sum) in der Task-Task abgeschlossen ist.

std::packaged_task

std::packaged_task ist eine Klassenvorlage zum Umschließen eines aufrufbaren Objekts (z. B. einer Funktion, eines Funktionsobjekts oder eines Lambda-Ausdrucks) und zum Verknüpfen mit einem std::future, sodass das aufrufbare Objekt in einem anderen Thread-Ergebnis abgerufen werden kann.

Zum Beispiel:

#include <iostream>
#include <future>
int calculateSum(int a, int b) {
    
    
    return a + b;
}
int main() {
    
    
    std::packaged_task<int(int, int)> task(calculateSum);
    std::future<int> result = task.get_future();
    // 启动异步任务
    std::thread thread(std::move(task), 2, 3);
    // 执行其他操作...
    // 获取异步任务的结果
    int sum = result.get();
    // 输出结果
    std::cout << "Sum: " << sum << std::endl;
    // 等待线程结束
    thread.join();
    return 0;
}

Ein Aufgabenobjekt wird mithilfe der Klassenvorlage std::packaged_task erstellt und die Funktion berechneSum als Parameter übergeben. Dann erhalten wir das damit verbundene std::future-Objektergebnis, indem wir task.get_future() aufrufen.

Es sieht so aus, als wären „packed_task“ und „promise“ ein bisschen wie oben, „ promise“ speichert einen Wert und „packed_task“ speichert eine Funktion.

Die Beziehung zwischen std::promise, std::packaged_task und std::future

Aus dem oben Gesagten ist ersichtlich, dass sowohl Promise als auch Packaged_Task .get_future() verwenden können, um Objekte abzurufen, da sie Futures enthalten, um auf die Operationsergebnisse zuzugreifen.
Promise speichert einen Wert und packet_task speichert eine Funktion. Wenn Sie einen bestimmten Wert erhalten möchten, verwenden Sie Promise. Wenn Sie einen Rückgabewert erhalten möchten, verwenden Sie packet_task (Sie können den Rückgabewert auch mit async abrufen, wie oben erwähnt).

Guess you like

Origin blog.csdn.net/weixin_44477424/article/details/132135734