A std::promise
ถูกสร้างขึ้นเป็นจุดสิ้นสุดสำหรับคู่สัญญา / อนาคตและstd::future
(สร้างจาก std :: สัญญาโดยใช้get_future()
วิธีการ) เป็นจุดสิ้นสุดอื่น ๆ นี่เป็นวิธีการยิงที่ง่ายวิธีหนึ่งในการจัดทำวิธีสำหรับสองเธรดเพื่อซิงโครไนซ์เนื่องจากเธรดหนึ่งให้ข้อมูลกับเธรดอื่นผ่านข้อความ
คุณสามารถคิดว่ามันเป็นหนึ่งเธรดสร้างสัญญาเพื่อให้ข้อมูลและหัวข้ออื่น ๆ รวบรวมสัญญาในอนาคต กลไกนี้สามารถใช้ได้เพียงครั้งเดียว
กลไกสัญญา / อนาคตเป็นเพียงทิศทางเดียวจากเธรดที่ใช้set_value()
วิธีการของ a std::promise
ไปยังเธรดที่ใช้get()
a ของstd::future
เพื่อรับข้อมูล ข้อยกเว้นจะถูกสร้างขึ้นหากget()
วิธีการในอนาคตถูกเรียกมากกว่าหนึ่งครั้ง
หากเธรดที่std::promise
ไม่ได้ใช้set_value()
เพื่อเติมเต็มสัญญาของมันแล้วเมื่อเธรดที่สองเรียกget()
ใช้std::future
เพื่อรวบรวมสัญญาเธรดที่สองจะเข้าสู่สถานะรอจนกว่าสัญญาจะเสร็จสมบูรณ์โดยเธรดแรกด้วยstd::promise
เมื่อใช้set_value()
เมธอด เพื่อส่งข้อมูล
ด้วย coroutines ที่เสนอของข้อมูลจำเพาะทางเทคนิค N4663 ภาษาโปรแกรม - ส่วนขยาย C ++ สำหรับ Coroutinesและการสนับสนุนคอมไพเลอร์ Visual Studio 2017 C ++ ของco_await
ยังเป็นไปได้ที่จะใช้std::future
และstd::async
เขียนฟังก์ชันการทำงานของ coroutine ดูการอภิปรายและเป็นตัวอย่างในhttps://stackoverflow.com/a/50753040/1466970ซึ่งมีเป็นส่วนหนึ่งที่กล่าวถึงการใช้ของที่มีstd::future
co_await
ตัวอย่างรหัสต่อไปนี้เป็นแอปพลิเคชันคอนโซล Windows Visual Studio 2013 แบบง่าย ๆ แสดงโดยใช้คลาส / แม่แบบการทำงานพร้อมกันของ C ++ 11 และฟังก์ชันอื่น ๆ มันแสดงให้เห็นถึงการใช้งานสำหรับสัญญา / อนาคตซึ่งทำงานได้ดีกระทู้อิสระที่จะทำงานบางอย่างและหยุดและการใช้งานที่จำเป็นต้องมีพฤติกรรมซิงโครนัสมากขึ้นและเนื่องจากความต้องการการแจ้งเตือนหลายคู่สัญญา / อนาคตไม่ทำงาน
หมายเหตุหนึ่งเกี่ยวกับตัวอย่างนี้คือความล่าช้าที่เพิ่มเข้ามาในสถานที่ต่าง ๆ ความล่าช้าเหล่านี้ถูกเพิ่มเข้ามาเท่านั้นเพื่อให้แน่ใจว่าข้อความต่าง ๆ ที่พิมพ์ไปยังคอนโซลที่ใช้std::cout
จะชัดเจนและข้อความจากกระทู้จำนวนมากจะไม่ถูกรวมเข้าด้วยกัน
ส่วนแรกของการmain()
สร้างสามกระทู้เพิ่มเติมและการใช้std::promise
และstd::future
การส่งข้อมูลระหว่างหัวข้อ จุดที่น่าสนใจคือที่ที่เธรดหลักเริ่มเธรด T2 ซึ่งจะรอข้อมูลจากเธรดหลักทำอะไรแล้วส่งข้อมูลไปยังเธรดที่สาม T3 ซึ่งจะทำบางสิ่งบางอย่างและส่งข้อมูลกลับไปที่ หัวข้อหลัก
ส่วนที่สองของการmain()
สร้างสองเธรดและชุดของคิวเพื่ออนุญาตให้หลายข้อความจากเธรดหลักไปยังแต่ละเธรดที่สร้างขึ้นสองรายการ เราไม่สามารถใช้งานได้std::promise
และstd::future
เพราะสิ่งนี้เพราะคำสัญญา / คู่หูในอนาคตเป็นนัดเดียวและไม่สามารถใช้ซ้ำได้
แหล่งที่มาของคลาสSync_queue
มาจากภาษา C ++ Programming Stroustrup: 4th Edition
// cpp_threads.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <iostream>
#include <thread> // std::thread is defined here
#include <future> // std::future and std::promise defined here
#include <list> // std::list which we use to build a message queue on.
static std::atomic<int> kount(1); // this variable is used to provide an identifier for each thread started.
//------------------------------------------------
// create a simple queue to let us send notifications to some of our threads.
// a future and promise are one shot type of notifications.
// we use Sync_queue<> to have a queue between a producer thread and a consumer thread.
// this code taken from chapter 42 section 42.3.4
// The C++ Programming Language, 4th Edition by Bjarne Stroustrup
// copyright 2014 by Pearson Education, Inc.
template<typename Ttype>
class Sync_queue {
public:
void put(const Ttype &val);
void get(Ttype &val);
private:
std::mutex mtx; // mutex used to synchronize queue access
std::condition_variable cond; // used for notifications when things are added to queue
std::list <Ttype> q; // list that is used as a message queue
};
template<typename Ttype>
void Sync_queue<Ttype>::put(const Ttype &val) {
std::lock_guard <std::mutex> lck(mtx);
q.push_back(val);
cond.notify_one();
}
template<typename Ttype>
void Sync_queue<Ttype>::get(Ttype &val) {
std::unique_lock<std::mutex> lck(mtx);
cond.wait(lck, [this]{return !q.empty(); });
val = q.front();
q.pop_front();
}
//------------------------------------------------
// thread function that starts up and gets its identifier and then
// waits for a promise to be filled by some other thread.
void func(std::promise<int> &jj) {
int myId = std::atomic_fetch_add(&kount, 1); // get my identifier
std::future<int> intFuture(jj.get_future());
auto ll = intFuture.get(); // wait for the promise attached to the future
std::cout << " func " << myId << " future " << ll << std::endl;
}
// function takes a promise from one thread and creates a value to provide as a promise to another thread.
void func2(std::promise<int> &jj, std::promise<int>&pp) {
int myId = std::atomic_fetch_add(&kount, 1); // get my identifier
std::future<int> intFuture(jj.get_future());
auto ll = intFuture.get(); // wait for the promise attached to the future
auto promiseValue = ll * 100; // create the value to provide as promised to the next thread in the chain
pp.set_value(promiseValue);
std::cout << " func2 " << myId << " promised " << promiseValue << " ll was " << ll << std::endl;
}
// thread function that starts up and waits for a series of notifications for work to do.
void func3(Sync_queue<int> &q, int iBegin, int iEnd, int *pInts) {
int myId = std::atomic_fetch_add(&kount, 1);
int ll;
q.get(ll); // wait on a notification and when we get it, processes it.
while (ll > 0) {
std::cout << " func3 " << myId << " start loop base " << ll << " " << iBegin << " to " << iEnd << std::endl;
for (int i = iBegin; i < iEnd; i++) {
pInts[i] = ll + i;
}
q.get(ll); // we finished this job so now wait for the next one.
}
}
int _tmain(int argc, _TCHAR* argv[])
{
std::chrono::milliseconds myDur(1000);
// create our various promise and future objects which we are going to use to synchronise our threads
// create our three threads which are going to do some simple things.
std::cout << "MAIN #1 - create our threads." << std::endl;
// thread T1 is going to wait on a promised int
std::promise<int> intPromiseT1;
std::thread t1(func, std::ref(intPromiseT1));
// thread T2 is going to wait on a promised int and then provide a promised int to thread T3
std::promise<int> intPromiseT2;
std::promise<int> intPromiseT3;
std::thread t2(func2, std::ref(intPromiseT2), std::ref(intPromiseT3));
// thread T3 is going to wait on a promised int and then provide a promised int to thread Main
std::promise<int> intPromiseMain;
std::thread t3(func2, std::ref(intPromiseT3), std::ref(intPromiseMain));
std::this_thread::sleep_for(myDur);
std::cout << "MAIN #2 - provide the value for promise #1" << std::endl;
intPromiseT1.set_value(22);
std::this_thread::sleep_for(myDur);
std::cout << "MAIN #2.2 - provide the value for promise #2" << std::endl;
std::this_thread::sleep_for(myDur);
intPromiseT2.set_value(1001);
std::this_thread::sleep_for(myDur);
std::cout << "MAIN #2.4 - set_value 1001 completed." << std::endl;
std::future<int> intFutureMain(intPromiseMain.get_future());
auto t3Promised = intFutureMain.get();
std::cout << "MAIN #2.3 - intFutureMain.get() from T3. " << t3Promised << std::endl;
t1.join();
t2.join();
t3.join();
int iArray[100];
Sync_queue<int> q1; // notification queue for messages to thread t11
Sync_queue<int> q2; // notification queue for messages to thread t12
std::thread t11(func3, std::ref(q1), 0, 5, iArray); // start thread t11 with its queue and section of the array
std::this_thread::sleep_for(myDur);
std::thread t12(func3, std::ref(q2), 10, 15, iArray); // start thread t12 with its queue and section of the array
std::this_thread::sleep_for(myDur);
// send a series of jobs to our threads by sending notification to each thread's queue.
for (int i = 0; i < 5; i++) {
std::cout << "MAIN #11 Loop to do array " << i << std::endl;
std::this_thread::sleep_for(myDur); // sleep a moment for I/O to complete
q1.put(i + 100);
std::this_thread::sleep_for(myDur); // sleep a moment for I/O to complete
q2.put(i + 1000);
std::this_thread::sleep_for(myDur); // sleep a moment for I/O to complete
}
// close down the job threads so that we can quit.
q1.put(-1); // indicate we are done with agreed upon out of range data value
q2.put(-1); // indicate we are done with agreed upon out of range data value
t11.join();
t12.join();
return 0;
}
แอปพลิเคชั่นที่เรียบง่ายนี้สร้างผลลัพธ์ต่อไปนี้
MAIN #1 - create our threads.
MAIN #2 - provide the value for promise #1
func 1 future 22
MAIN #2.2 - provide the value for promise #2
func2 2 promised 100100 ll was 1001
func2 3 promised 10010000 ll was 100100
MAIN #2.4 - set_value 1001 completed.
MAIN #2.3 - intFutureMain.get() from T3. 10010000
MAIN #11 Loop to do array 0
func3 4 start loop base 100 0 to 5
func3 5 start loop base 1000 10 to 15
MAIN #11 Loop to do array 1
func3 4 start loop base 101 0 to 5
func3 5 start loop base 1001 10 to 15
MAIN #11 Loop to do array 2
func3 4 start loop base 102 0 to 5
func3 5 start loop base 1002 10 to 15
MAIN #11 Loop to do array 3
func3 4 start loop base 103 0 to 5
func3 5 start loop base 1003 10 to 15
MAIN #11 Loop to do array 4
func3 4 start loop base 104 0 to 5
func3 5 start loop base 1004 10 to 15