hapter 18
Concurrency
Modern system architectures usually support running multiple tasks and multiple threads at the same
time. Especially when multiple processor cores are provided, the execution time of programs can
significantly improve when multiple threads are used.
However, executing things in parallel also introduces new challenges. Instead of doing one statement
after the other, multiple statements can be performed simultaneously, which can result in such
problems as concurrently accessing the same resources, so that creations, reads, writes, and deletions
don’t take place in an expected order and provide unexpected results. In fact, concurrent access
to data from multiple threads easily can become a nightmare, with such problems as deadlocks,
whereby threads wait for each other, belonging to the simple cases.
Before C++11, there was no support for concurrency in the language and the C++ standard
library, although implementations were free to give some guarantees. With C++11, this has changed.
Both the core language and the library were improved to support concurrent programming (see
Section 4.5, page 55):
• The core language now defines a memory model that guarantees that updates on two different
objects used by two different threads are independent of each other, and has introduced a new
keyword thread_local for defining variables with thread-specific values.
• The library now provides support to start multiple threads, including passing arguments, return
values, and exceptions across thread boundaries, as well as means to synchronize multiple
threads, so we can synchronize both the control flow and data access.
The library provides its support on different levels. For example, a high-level interface allows you to
start a thread including passing arguments and dealing with results and exceptions, which is based on
a couple of low-level interfaces for each of these aspects. On the other hand, there are also low-level
features, such as mutexes or even atomics dealing with relaxed memory orders.
This chapter introduces these library features. Note that the topic of concurrency and the description
of the libraries provided for it can fill books. So, here, I introduce general concepts and typical
examples for the average application programmer, with the main focus on the high-level interfaces.
For any details, especially of the tricky low-level problems and features, please refer to the specific
books and articles mentioned. My first and major recommendation for this whole topic of concurrency
is the book C++ Concurrency in Action by Anthony Williams (see [Williams:C++Conc]).
www.it-ebooks.info
946 Chapter 18: Concurrency
Anthony is one of the world key experts on this topic, and this chapter would not have been possible
without him. Besides a preview of his book, he provided a first implementation of the standard
concurrency library (see [JustThread]), wrote several articles, and gave valuable feedback, which all
helped me to present this topic in what, hopefully, is a useful way. But in addition, I’d like to thank
a few other concurrency experts who helped me to write this chapter: Hans Boehm, Scott Meyers,
Bartosz Milewski, Lawrence Crowl, and Peter Sommerlad.
The chapter is organized as follows:
• First, I introduce various ways to start multiple threads. After both the high-level and the lowlevel
interfaces are introduced, details of starting a thread are presented.
• Section 18.4, page 982, offers a detailed discussion of the problem of synchronizing threads. The
main problem is concurrent data access.
• Finally, various features to synchronize threads and concurrent data access are discussed:
– Mutexes and locks (see Section 18.5, page 989), including call_once() (see Section 18.5.3,
page 1000)
– Condition variables (see Section 18.6, page 1003)
– Atomics (see Section 18.7, page 1012)
18.1 The High-Level Interface: async() and
Futures
For novices, the best starting point to run your program with multiple threads is the high-level interface
of the C++ standard library provided by std::async() and class std::future:
• async() provides an interface to let a piece of functionality, a callable object (see Section 4.4,
page 54), run in the background as a separate thread, if possible.
• Class future allows you to wait for the thread to be finished and provides access to its outcome:
return value or exception, if any.
This section introduces this high-level interface in detail, extended by an introduction to class
std::shared_future, which allows you to wait for and process the outcome of a thread at
multiple places.
18.1.1 A First Example Using async() and Futures
Suppose that we have to compute the sum of two operands returned by two function calls. The usual
way to program that would be as follows:
func1() + func2()
This means that the processing of the operands happens sequentially. The program will first call
func1() and then call func2() or the other way round (according to language rules, the order is
undefined). In both cases, the overall processing takes the duration of func1() plus the duration of
func2() plus computing the sum.
www.it-ebooks.info
18.1 The High-Level Interface: async() and Futures 947
These days, using the multiprocessor hardware available almost everywhere, we can do better.
We can at least try to run func1() and func2() in parallel so that the overall duration takes only
the maximum of the duration of func1() and func2() plus processing the sum.
Here is a first program doing that:
// concurrency/async1.cpp
#include
#include
#include
#include
#include
#include
using namespace std;
int doSomething (char c)
{
// random-number generator (use c as seed to get different sequences)
std::default_random_engine dre(c);
std::uniform_int_distribution id(10,1000);
// loop to print character after a random period of time
for (int i=0; i
hapter 18
Concurrency
สถาปัตยกรรมระบบสมัยใหม่มักจะสนับสนุนการใช้งานหลายรูปแบบและหลายหัวข้อที่เดียวกัน
เวลา โดยเฉพาะอย่างยิ่งเมื่อแกนประมวลผลหลายให้เวลาดำเนินการของโปรแกรมสามารถ
ปรับปรุงอย่างมีนัยสำคัญเมื่อหลายหัวข้อที่จะใช้ใน
การดำเนินการในสิ่งที่คู่ขนานยังแนะนำความท้าทายใหม่ แทนการทำคำสั่งอย่างใดอย่างหนึ่ง
หลังจากที่อื่น ๆ งบหลายสามารถดำเนินการพร้อมกันซึ่งจะส่งผลใน
ปัญหาที่เป็นที่เห็นพ้องกันในการเข้าถึงทรัพยากรเดียวกันเพื่อให้การสร้าง, อ่าน, เขียนและลบ
ไม่ได้ใช้สถานที่ในการสั่งซื้อที่คาดหวังและให้ ผลที่ไม่คาดคิด ในความเป็นจริงการเข้าถึงพร้อมกัน
ข้อมูลจากหลายหัวข้อที่สามารถกลายเป็นฝันร้ายที่มีปัญหาเช่นการติดตาย,
หัวข้อโดยรอให้กันและกันเป็นกรณีที่ง่าย
ก่อน C + +11, มีการสนับสนุนสำหรับการทำงานพร้อมกันในภาษา และ C + + มาตรฐาน
ห้องสมุดแม้ว่าการใช้งานที่มีอิสระที่จะให้การค้ำประกันบางส่วน กับ C + +11 นี้มีการเปลี่ยนแปลง
ทั้งภาษาหลักและห้องสมุดได้รับการปรับปรุงให้ดีขึ้นเพื่อสนับสนุนการเขียนโปรแกรมพร้อมกัน (ดู
มาตรา 4.5, หน้า 55):
•ภาษาหลักในขณะนี้กำหนดรูปแบบของหน่วยความจำที่รับประกันได้ว่าการปรับปรุงในสองที่แตกต่างกัน
ของวัตถุที่ใช้ สองหัวข้อที่แตกต่างกันมีความเป็นอิสระของแต่ละอื่น ๆ และได้แนะนำใหม่
thread_local คำหลักสำหรับการกำหนดตัวแปรที่มีค่าด้ายเฉพาะ
•ห้องสมุดตอนนี้ให้การสนับสนุนในการเริ่มต้นหลายหัวข้อรวมทั้งข้อโต้แย้งที่ผ่านการกลับ
ค่านิยมและข้อยกเว้นในขอบเขตด้าย เช่นเดียวกับที่หมายถึงการประสานหลาย
หัวข้อเพื่อให้เราสามารถประสานทั้งการควบคุมการไหลและข้อมูลที่เข้าถึง
ห้องสมุดที่ให้การสนับสนุนในระดับที่แตกต่างกัน ตัวอย่างเช่นอินเตอร์เฟซระดับสูงช่วยให้คุณสามารถ
เริ่มต้นด้ายรวมทั้งผ่านการขัดแย้งและการจัดการกับผลและข้อยกเว้นซึ่งจะขึ้นอยู่กับ
คู่ของการเชื่อมต่อในระดับต่ำสำหรับแต่ละด้านเหล่านี้ ในขณะที่ยังมีระดับต่ำ
คุณสมบัติเช่น mutexes หรือแม้แต่อะตอมจัดการกับคำสั่งซื้อหน่วยความจำที่ผ่อนคลาย
ในบทนี้จะแนะนำคุณสมบัติห้องสมุดเหล่านี้ โปรดทราบว่าหัวข้อของการทำงานพร้อมกันและคำอธิบาย
ของห้องสมุดที่ให้ไว้เพื่อที่จะสามารถเติมเต็มหนังสือ ดังนั้นที่นี่ฉันแนะนำแนวคิดทั่วไปและโดยทั่วไป
ตัวอย่างสำหรับโปรแกรมประยุกต์โดยเฉลี่ยแล้วมีการเน้นหลักในการเชื่อมต่อระดับสูง
สำหรับรายละเอียดใด ๆ โดยเฉพาะอย่างยิ่งปัญหาในระดับต่ำที่ยุ่งยากและคุณสมบัติโปรดดูเฉพาะ
หนังสือและ บทความที่กล่าวถึง คำแนะนำแรกของฉันและที่สำคัญสำหรับหัวข้อนี้ทั้งหมดของการทำงานพร้อมกัน
เป็นหนังสือ C + + พ้องในการดำเนินการโดยแอนโทนี่วิลเลียมส์ (ดู [วิลเลียมส์: C + + ความเข้มข้น])
www.it-ebooks.info
946 บทที่ 18: Concurrency
แอนโทนี่เป็นหนึ่งใน ผู้เชี่ยวชาญด้านที่สำคัญของโลกในหัวข้อนี้และบทนี้จะไม่ได้รับเป็นไปได้
โดยไม่มีเขา นอกจากนี้ตัวอย่างของหนังสือของเขาที่เขาให้การดำเนินการครั้งแรกของมาตรฐาน
ห้องสมุดพ้อง (ดู [JustThread]) เขียนบทความหลายและให้ข้อเสนอแนะที่มีคุณค่าที่ทุกคน
ช่วยให้ผมที่จะนำเสนอหัวข้อนี้ในสิ่งที่หวังว่าเป็นวิธีที่มีประโยชน์ . แต่ในนอกจากนี้ผมอยากจะขอบคุณ
ไม่กี่ผู้เชี่ยวชาญเห็นพ้องอื่น ๆ ที่ช่วยให้ผมเขียนบทนี้: ฮันส์ Boehm สกอตต์เมเยอร์ส
Bartosz Milewski อเรนซ์ Crowl และปีเตอร์ Sommerlad
บทที่มีการจัดดังนี้
•ครั้งแรกผม แนะนำวิธีการต่างๆที่จะเริ่มต้นหลายหัวข้อ หลังจากที่ทั้งระดับสูงและ lowlevel
อินเตอร์เฟซที่ได้รับการแนะนำให้รู้จักกับรายละเอียดของการเริ่มต้นด้ายนั้นจะมี
•มาตรา 18.4, หน้า 982 มีการอภิปรายรายละเอียดของปัญหาตรงกันหัวข้อ
ปัญหาหลักคือการเข้าถึงข้อมูลพร้อมกัน
•ในที่สุดคุณสมบัติต่างๆเพื่อประสานหัวข้อและการเข้าถึงข้อมูลพร้อมกันจะกล่าวถึง:
- mutexes และล็อค (ดูมาตรา 18.5, หน้า 989) รวมทั้ง call_once () (ดูมาตรา 18.5.3,
หน้า 1000)
- สภาพตัวแปร (ดูมาตรา 18.6, หน้า 1003)
- อะตอม (ดูมาตรา 18.7, หน้า 1012)
18.1 ระดับสูง Interface: async () และ
สัญญาซื้อขายล่วงหน้า
สำหรับสามเณรเป็นจุดเริ่มต้นที่ดีที่สุดเพื่อเรียกใช้โปรแกรมของคุณด้วยหลายหัวข้อที่อยู่ในระดับสูง อินเตอร์เฟซระดับ
ของห้องสมุด C + + มาตรฐานการให้บริการโดย std :: async () และมาตรฐานการเรียนในอนาคต :: <>:
• async () มีอินเตอร์เฟซเพื่อให้ชิ้นส่วนของการทำงานวัตถุ callable (ดูมาตรา 4.4,
หน้า 54) , ทำงานในพื้นหลังเป็นหัวข้อที่แยกต่างหากถ้าเป็นไปได้
ในอนาคต•คลาส <> ช่วยให้คุณสามารถที่จะรอให้ด้ายจะแล้วเสร็จและให้การเข้าถึงผลของมัน
ค่าตอบแทนหรือข้อยกเว้นถ้ามี
ในส่วนนี้จะนำเสนอในระดับสูงนี้ อินเตอร์เฟซในรายละเอียดขยายโดยการแนะนำไปเรียน
std :: shared_future <> ซึ่งช่วยให้คุณรอการประมวลผลและผลของด้ายที่
สถานที่ต่างๆ
18.1.1 การซื้อแรกตัวอย่างการใช้ async () และฟิวเจอร์ส
สมมติว่าเรามี การคำนวณผลรวมของทั้งสองตัวถูกดำเนินการส่งกลับโดยสองสายงาน ตามปกติ
วิธีการเขียนโปรแกรมที่จะเป็นดังนี้
func1 () + Func2 ()
ซึ่งหมายความว่าการประมวลผลของตัวถูกดำเนินการที่เกิดขึ้นตามลำดับ โปรแกรมแรกที่จะเรียก
func1 () แล้วโทร Func2 () หรือรอบทางอื่น ๆ (ตามกฎภาษาเพื่อที่จะ
ไม่ได้กำหนด) ในทั้งสองกรณีการประมวลผลโดยรวมจะใช้เวลาระยะเวลาของการ func1 () บวกกับระยะเวลาของการ
Func2 () บวกกับการคำนวณผลรวม
www.it-ebooks.info
18.1 ระดับสูง Interface: async () และฟิวเจอร์ส 947
วันนี้ใช้ ฮาร์ดแวร์หลายตัวที่มีอยู่เกือบทุกที่เราสามารถทำได้ดีกว่า
เราสามารถอย่างน้อยพยายามที่จะเรียก func1 () และ Func2 () ในแบบคู่ขนานเพื่อให้ระยะเวลาโดยรวมจะใช้เวลาเพียง
ไม่เกินระยะเวลาของ func1 () และ Func2 () บวกกับการประมวลผล ผลรวม
ที่นี่เป็นโปรแกรมแรกที่ทำที่:
/ / concurrency/async1.cpp
# include
# include
# include
# include
# include
# include
โดยใช้ namespace std;
int doSomething (ถ่านค)
{
/ / สุ่มหมายเลขเครื่องกำเนิดไฟฟ้า (ใช้คเป็นเมล็ดพันธุ์ที่จะได้รับลำดับที่แตกต่างกัน)
std :: default_random_engine ดรี (ค);
std :: uniform_int_distributionรหัส (10,1000);
/ / ห่วงในการพิมพ์ตัวอักษรหลังจากสุ่มช่วงเวลา
for (int i = 0; ฉัน <10; + + i) {
this_thread :: sleep_for (โครโนกราฟ :: มิลลิวินาที (id (ดรี)) );
. cout.put (c) ล้าง ();
}
กลับ c;
}
int func1 ()
{
กลับ doSomething () '.';
}
int Func2 ()
{
กลับ doSomething ('+');
}
int หลัก ()
{
STD :: ศาล << "เริ่มต้น func1 () ในพื้นหลัง"
<< "และ Func2 () ในเบื้องหน้า:" << std :: endl;
www.it-ebooks.info
948 บทที่ 18: Concurrency
/ / เริ่มต้น func1 ( ) ถ่ายทอดสด (ตอนนี้หรือในภายหลังหรือไม่):
std :: อนาคตresult1 (std :: async (func1));
result2 int = Func2 () / / โทร Func2 () พร้อมกัน (ที่นี่และตอนนี้)
ผล / / พิมพ์ (รอ func1 () เพื่อจบและเพิ่มผลในการ result2
ผล int = result1.get () + result2;
STD :: ศาล << " nresult ของ func1 () + Func2 ():" << ผล
<< std :: endl;
}
เพื่อให้เห็นภาพสิ่งที่เกิดขึ้นเราจำลอง processings ซับซ้อนใน func1 () และ Func2 () โทร
doSomething () ซึ่งเวลาพิมพ์ตัวอักษรผ่านเป็น argument1 และในที่สุดก็
ส่งกลับค่าของตัวละครผ่านการเป็น int. "เวลา" จะดำเนินการใช้
เครื่องกำเนิดไฟฟ้าแบบสุ่มจำนวน เพื่อระบุช่วงเวลาซึ่ง std :: this_thread :: sleep_for () ใช้เป็น
หมดเวลาสำหรับเธรดปัจจุบัน (ดูมาตรา 17.1, หน้า 907 สำหรับรายละเอียดของตัวเลขสุ่มและ
มาตรา 18.3.7, หน้า 981 สำหรับรายละเอียดของ sleep_for () .) โปรดทราบว่าเราจำเป็นต้องมีเมล็ดพันธุ์ที่ไม่ซ้ำกันสำหรับ
การสร้างของเครื่องกำเนิดไฟฟ้าจำนวนสุ่ม (ที่นี่เราจะใช้ตัวละครผ่านค) เพื่อให้แน่ใจว่า
ลำดับที่สร้างแบบสุ่มจำนวนที่แตกต่างกัน
แทนการเรียก:
ผล int = func1 () + Func2 ();
เราเรียก:
std :: อนาคตresult1 (std :: async (func1));
result2 int = Func2 ();
ผล int = result1.get () + result2;
ดังนั้นครั้งแรกที่เราพยายามที่จะเริ่มต้น func1 () ในพื้นหลังโดยใช้ std :: async ( ) และกำหนดผลการ
เป้าหมายของมาตรฐานชั้น :: อนาคต:
std :: อนาคตresult1 (std :: async (func1));
นี่ async () พยายามที่จะเริ่มต้นการทำงานได้ทันทีผ่านการถ่ายทอดสดในที่แยกต่างหาก
ด้าย ดังนั้น func1 () นึกคิดเริ่มต้นที่นี่โดยไม่ปิดกั้น (ฟังก์ชั่นหลัก) กลับ
วัตถุอนาคตเป็นสิ่งจำเป็นสำหรับเหตุผลสองประการคือ
1 จะช่วยให้การเข้าถึง "อนาคต" ผลของการทำงานที่ส่งผ่านไปยัง async () ผลที่ตามมา
อาจจะเป็นได้ทั้งค่าตอบแทนหรือข้อยกเว้น วัตถุในอนาคตได้รับการเฉพาะโดย
ประเภทการกลับมาของฟังก์ชั่นเริ่มต้น ถ้างานพื้นหลังเพียงแค่เริ่มต้นที่ส่งกลับไม่มีอะไร
ก็จะต้องมี std :: อนาคต.
2 มันเป็นสิ่งที่จำเป็นเพื่อให้แน่ใจว่าไม่ช้าก็เร็วฟังก์ชันการทำงานที่ผ่านมาได้รับการเรียกว่า โปรดทราบว่าฉัน
เขียนว่า async () พยายามที่จะเริ่มต้นการทำงานที่ผ่านมา ถ้าไม่ได้เกิดขึ้นเราต้อง
วัตถุในอนาคตที่จะบังคับให้เริ่มต้นเมื่อเราต้องการผลหรือต้องการที่จะให้แน่ใจว่าการทำงานเป็น
1 ออกโดยหัวข้อพร้อมกันเป็นไปได้ แต่อาจส่งผลให้ตัวละครอินเตอร์ (ดูมาตรา 4.5, หน้า 56)
www.it-ebooks.info
18.1 การเชื่อมต่อระดับสูง: async () และฟิวเจอร์ส 949
ดำเนินการ ดังนั้นคุณจำเป็นต้องใช้วัตถุในอนาคตแม้ว่าคุณจะไม่สนใจในผลของ
การทำงานเริ่มต้นในพื้นหลัง
เพื่อให้สามารถแลกเปลี่ยนข้อมูลระหว่างสถานที่ที่จะเริ่มต้นและควบคุมการทำงานและ
วัตถุในอนาคตกลับมาทั้งสองอ้างถึงดังนั้น ที่เรียกว่ารัฐที่ใช้ร่วมกัน (ดูมาตรา 18.3, หน้า 973)
และแน่นอนว่าคุณยังสามารถและมักจะใช้รถยนต์ที่จะประกาศในอนาคต (ฉันอย่างชัดเจนต้องการที่จะ
แสดงให้เห็นถึงประเภทของที่นี่):
result1 อัตโนมัติ (std :: async (func1 ));
ประการที่สองเราเริ่มต้น Func2 () ในเบื้องหน้า นี่คือการเรียกใช้ฟังก์ชันจังหวะปกติเพื่อให้
บล็อกโปรแกรมที่นี่:
result2 int = Func2 ();
ดังนั้นหาก func1 () ประสบความสำเร็จเริ่มจาก async () และไม่ได้จบลงแล้วตอนนี้เรามี
func1 () และ Func2 ( ) ทำงานในขนาน
ที่สามเราประมวลผลรวม นี่คือช่วงเวลาเมื่อเราต้องการผลของ func1 () จะได้รับมัน
ที่เราเรียกได้รับ () สำหรับอนาคตกลับ:
ผล int = result1.get () + result2;
ที่นี่มีการเรียกร้องของรับ () ซึ่งเป็นหนึ่งในสามสิ่งที่อาจจะเกิดขึ้น:
1 หาก func1 () เริ่มต้นด้วย async () ในหัวข้อที่แยกต่างหากและมีการดำเนินการเสร็จสิ้นแล้วคุณได้ทันที
จะได้รับผลของ
2 หาก func1 () เริ่มต้น แต่ยังไม่ได้ดำเนินการเสร็จสิ้นได้รับ (บล็อค) และรอให้สิ้นสุดและทำให้
ผล
3 หาก func1 () แล้วยังไม่ได้เริ่มก็จะข
การแปล กรุณารอสักครู่..

hapter 18
ระบบทันสมัยสถาปัตยกรรมมักจะสนับสนุนการใช้หลายงานและหลายหัวข้อในเวลาเดียวกัน
โดยเฉพาะอย่างยิ่งเมื่อแกนประมวลผลหลาย ให้ การ เวลาของโปรแกรมสามารถปรับปรุงเมื่อหลายกระทู้
แต่การใช้ สิ่งในขนานนี้ยังเสนอความท้าทายใหม่ แทนที่จะทำชี้แจง
หลังจากที่อื่น ๆหลายข้อที่สามารถดำเนินการได้พร้อมกัน ซึ่งสามารถส่งผลให้ปัญหาดังกล่าวจากการเข้าถึงทรัพยากร
เป็นเหมือนกัน ดังนั้นการสร้าง , อ่าน , เขียนและลบ
ไม่ใช้สถานที่ในการคิดและเพื่อให้ผลลัพธ์ที่ไม่คาดคิด ในความเป็นจริง การเข้าถึง
ข้อมูลจากหลายกระทู้สามารถกลายเป็นฝันร้าย ที่มีปัญหา เช่น ติดตาย
,ซึ่งกระทู้รอกันและกัน เป็นกรณีง่ายก่อน C .
11 ไม่มีการสนับสนุนพร้อมกันในภาษา c มาตรฐานห้องสมุด
ถึงแม้ว่าการใช้งานได้ฟรีเพื่อให้การันตี กับ ซี 11 นี้มีการเปลี่ยนแปลง
ทั้งหลักภาษาและห้องสมุดได้รับการปรับปรุงเพื่อสนับสนุนโปรแกรมพร้อมกัน ( ดู
ส่วน 4.5 , หน้า 55 ) :
- หลักภาษา ตอนนี้กำหนดหน่วยความจำแบบที่รับประกันว่าข้อมูลที่แตกต่างกันสอง
วัตถุที่ใช้โดยสองหัวข้อที่แตกต่างกันเป็นอิสระของแต่ละอื่น ๆ และมีการแนะนำคำหลักใหม่
thread_local สําหรับการกําหนดตัวแปรที่มีค่าเฉพาะหัวข้อ .
- ห้องสมุดตอนนี้ให้สนับสนุนการเริ่มต้นหลายๆ กระทู้ รวมทั้งผ่านอาร์กิวเมนต์กลับ
ค่า ,และข้อยกเว้นในหัวข้อขอบเขต รวมทั้งวิธีการที่จะประสานหลาย
กระทู้ ดังนั้นเราสามารถประสานทั้งการควบคุมการไหลและการเข้าถึงข้อมูล .
ห้องสมุดมีการสนับสนุนในระดับที่แตกต่างกัน ตัวอย่างเช่นมีอินเตอร์เฟซและช่วยให้คุณ
เริ่มหัวข้อรวมทั้งผ่านอาร์กิวเมนต์ และเผชิญกับผลลัพธ์และข้อยกเว้น ซึ่งขึ้นอยู่กับ
คู่ของคนระดับล่าง ภายใต้แต่ละประเด็นเหล่านี้ บนมืออื่น ๆ นอกจากนี้ยังมีคุณสมบัติระดับ
เช่น mutexes หรือแม้แต่อะตอมเผชิญกับผ่อนคลายคำสั่งหน่วยความจำ .
บทนี้เปิดตัวห้องสมุดองค์ประกอบเหล่านี้ ทราบว่าหัวข้อของการเห็นพ้องด้วยและรายละเอียด
ของห้องสมุดให้สามารถกรอกหนังสือ ดังนั้นที่นี่ฉันแนะนำแนวคิดทั่วไปและโดยทั่วไป
ตัวอย่างสำหรับโปรแกรมเมอร์โปรแกรมปานกลาง เน้นหลักในการเชื่อมต่อระดับสูง
รายละเอียดใด ๆ โดยเฉพาะอย่างยิ่ง ของหากิน มี ปัญหา และคุณลักษณะ กรุณาอ้างถึงหนังสือที่เฉพาะเจาะจง
และบทความที่กล่าวถึง ของฉันแรกและหลักแนวทางของการ
หัวข้อทั้งหมดนี้เป็นหนังสือ C พร้อมกันในการกระทำโดย Anthony Williams ( ดู [ วิลเลียมส์ : C เข้มข้น ] )
wwwมันอี - บุ๊ค ข้อมูล
946 บทที่ 18 : การ
แอนโทนี่เป็นหนึ่งของโลกที่สำคัญผู้เชี่ยวชาญในหัวข้อนี้ และบทนี้จะไม่ได้รับเป็นไปได้
โดยไม่มีเขา นอกจากนี้ตัวอย่างของหนังสือของเขา เขาให้ใช้งานครั้งแรกของห้องสมุดการมาตรฐาน
( ดู [ justthread ] ) เขียนบทความหลายและให้ข้อเสนอแนะที่มีคุณค่า ซึ่งทั้งหมด
ช่วยเสนอหัวข้อนี้ในสิ่งที่หวังเป็นวิธีที่มีประโยชน์ แต่นอกจากนี้ผมอยากขอบคุณ
ไม่กี่อื่น ๆ ผู้เชี่ยวชาญด้านการ ที่ช่วยฉันเขียนบทนี้ : ฮันส์ โบม สก็อต ไมเยอร์
bartosz milewski ลอว์เรนซ์ crowl และปีเตอร์ sommerlad .
บทจัดดังนี้
- ครั้งแรกผมแนะนำวิธีการต่าง ๆเพื่อเริ่มต้นหลายกระทู้ หลังจากทั้งระดับสูงและระดับต่ำ
interfaces แนะนํารายละเอียดของการเริ่มต้นหัวข้อ นำเสนอ
- ส่วน 18.4 , หน้า 982 , เสนอการอภิปรายรายละเอียดของปัญหาของการกระทู้
ปัญหาหลักคือการเข้าถึงข้อมูล .
- ในที่สุด คุณสมบัติต่าง ๆ เช่น หัวข้อ และการเข้าถึงข้อมูลการอภิปราย :
- mutexes และล็อค ( ดูมาตรา 18 หน้า 989 ) ได้แก่ call_once() ( ดูมาตรา 18.5.3 หน้า 1
, )- เงื่อนไขตัวแปร ( ดูมาตรา 18.6 , หน้า 1002 )
- อะตอม ( ดูมาตรา 18.7 , หน้า 1 )
18.1 ระดับอินเตอร์เฟซ : async()
และล่วงหน้าสำหรับสามเณรจุดเริ่มต้นที่ดีที่สุดเพื่อเรียกใช้โปรแกรมของคุณด้วยหลายกระทู้เป็นระดับสูงติดต่อ
ของมาตรฐานห้องสมุดโดย async() std : : และคลาส std : : อนาคต < > :
- async() มีอินเตอร์เฟซเพื่อให้ชิ้นส่วนของการทํางานวัตถุคง ( ดูมาตรา 4.4
หน้า 54 ) ทำงานในพื้นหลังเป็นหัวข้อแยกต่างหาก ถ้าเป็นไปได้
- คลาสในอนาคต < > ช่วยให้คุณรอด้ายจะแล้วเสร็จและมีการเข้าถึงผล :
คืนค่าหรือข้อยกเว้น ถ้าใด ๆ .
ส่วนนี้แนะนำอินเตอร์เฟซระดับสูงนี้ ในรายละเอียด การแนะนำคลาส
std : : shared_future < > ,ซึ่งช่วยให้คุณรอและกระบวนการผลของกระทู้ที่
18.1.1 แรกสถานที่หลาย ตัวอย่างการใช้ async() และล่วงหน้า
สมมติว่า เราต้องคำนวณผลรวมของ 2 เปอแรนด์ที่ส่งกลับโดยฟังก์ชันสองสาย วิธีปกติ
โปรแกรมจะเป็นดังนี้ func1() func2()
ซึ่งหมายความว่าการประมวลผลของเปอแรนด์เกิดขึ้นตามลำดับ . โปรแกรมจะเรียก
func1() แล้วโทร func2() หรือรอบทางอื่น ๆ ( ตามกฎ , ภาษาคำสั่ง
Prayer ) ในทั้งสองกรณี , การประมวลผลโดยรวมใช้ระยะเวลา func1() บวกระยะเวลา
func2() พลัสคำนวณผลรวม www.it-ebooks
และ ข้อมูลระดับอินเตอร์เฟซ : async() และฟิวเจอร์ส 947
วันเหล่านี้ การใช้มัลติฮาร์ดแวร์ที่มีอยู่เกือบทุกที่ เราสามารถทำได้ดีกว่านี้
อย่างน้อยเราก็พยายามวิ่งและ func1() func2() ขนานเพื่อให้ระยะเวลารวมใช้เวลาเพียง
สูงสุดของระยะเวลาของ func1() func2() บวกและประมวลผลผลรวม .
ที่นี่เป็นครั้งแรกโปรแกรมทำ :
/ / /
#พร้อมกัน async1 . cpp >
< อนาคต รวมถึง# >
< หัวข้อรวม #รวมถึง < เวลา >
< >
##รวมถึงการรวม iostream >
< >
< #รวมถึงข้อยกเว้นการใช้ namespace std ;
dosomething ( char int c )
{
/ / แบบสุ่มจำนวน Generator ( ใช้ C เป็นเมล็ดพันธุ์เพื่อให้ได้ลำดับต่างกัน )
std : : default_random_engine เดร์ ( c ) ;
std : : uniform_int_distribution < int > ID ( การสูญเสียตำแหน่งงาน ) ;
/ / ลูปเพื่อพิมพ์ตัวอักษรหลังจากสุ่มระยะเวลา
( int ฉัน = 0 ; ฉัน < 10 ; I ) {
this_thread : : sleep_for ( Chrono : : มิลลิวินาที ( ID ( DRE ) ) ) ;
เคาท์ ใส่ ( C ) flush() ;
}
กลับมา C ;
}
{
func1 int ( ) ผลตอบแทน dosomething ( '
' ) ; }
func2 ( int )
{
กลับ dosomething ( ' ' ) ;
} {
1 main() std : : เคาท์ < < " เริ่ม func1() ในพื้นหลัง "
< < " และ func2() ในเบื้องหน้า " < < std : : Endl ;
www.it-ebooks ข้อมูล
882 บทที่ 18 : การเห็นพ้องด้วย
/ / เริ่ม func1() อะ ( ตอนนี้หรือในภายหลังหรือไม่ ) :
std : : INT > result1 ในอนาคต < ( std : : การ ( func1 ) ) ;
1 result2 = func2() ; / / โทร func2() synchronously ( ตอนนี้ )
/ / พิมพ์ผล ( รอ func1() เสร็จสิ้น และเพิ่มผลของ result2
1 ผล = result1 . get() result2 ;
std : : เคาท์ < < " nresult ของ func1() func2() " < < ผล
< < std : : Endl ;
}
เห็นภาพว่าเกิดอะไรขึ้น เราใช้มาตรฐานที่ซับซ้อน และใน func1() func2() เรียก
dosomething() ซึ่งเวลาพิมพ์ตัวอักษรผ่านเป็น argument1
และในที่สุดส่งกลับค่าของผ่านตัวละครเป็น Int . " เวลา " จะดำเนินการโดยใช้
การสร้างตัวเลขสุ่มเพื่อระบุช่วงเวลาที่ this_thread STD : : : : sleep_for() ใช้
หมดเวลาสำหรับเธรดปัจจุบัน ( ดูส่วน 17.1 , หน้าคุณๆ สําหรับรายละเอียดของตัวเลขสุ่มและ
ส่วน 18.3.7 981 หน้า สำหรับรายละเอียดของ sleep_for() ) โปรดทราบว่าเราต้องการเมล็ดพันธุ์ที่ไม่ซ้ำกันสำหรับ
ผู้สร้างของการสร้างตัวเลขสุ่ม ( ที่นี่เราใช้ผ่านตัวอักษร C ) เพื่อให้แน่ใจว่าสร้างเลขสุ่มลำดับแตกต่าง
.
แทนที่จะโทร :
1 ผล = func1() func2() ;
เราโทร : STD : : INT > result1 ในอนาคต < ( std : : การ ( func1 ) ) ;
! result2 = func2() ;
1 ผล = result1 . get() result2 ;
ก่อนอื่น เราลองมาเริ่ม func1() ในพื้นหลัง , การใช้ async() std : : ,และกำหนดผล
วัตถุของคลาส std : : อนาคต :
STD : : INT > result1 ในอนาคต < ( std : : การ ( func1 ) ) ;
ที่นี่ async() พยายามที่จะเริ่มการทำงานทันทีผ่านอะในหัวข้อแยกต่างหาก
ดังนั้น func1() ความนึกคิดเริ่มต้นที่นี่โดยไม่ปิดกั้นการทำงาน main() . คืนวัตถุจำเป็น
ในอนาคตสำหรับสองเหตุผล :
1มันช่วยให้เข้าถึง " ผลของการทํางานผ่าน async() ในอนาคต " นี้ผล
อาจเป็นได้ทั้งคืนค่าหรือข้อยกเว้น เป้าหมายในอนาคต มีความเชี่ยวชาญ โดย
กลับชนิดของการทำงานที่เริ่มต้น ถ้าแค่พื้นหลังงานเริ่มที่ส่งกลับไม่มีอะไร
มี std : : > โมฆะในอนาคต < .
2 มันเป็นสิ่งที่จำเป็นเพื่อให้มั่นใจว่า ไม่ช้าก็เร็วผ่านฟังก์ชันถูกเรียก หมายเหตุที่ผม
เขียนว่า async() พยายามเริ่มผ่านการทํางาน ถ้ามันไม่เกิดขึ้น เราต้องการวัตถุ
ในอนาคตที่จะบังคับให้เริ่มต้นเมื่อเราต้องการผล หรือต้องการให้แน่ใจว่า การทำงานคือ
1 ออกโดยหัวข้อพร้อมกันเป็นไปได้ แต่อาจส่งผลในอัดตัวละคร ( ดูในส่วน 4.5 , หน้า 56 )
www.it-ebooks ข้อมูล
181 ระดับอินเตอร์เฟซ : async() และฟิวเจอร์ส 949
แสดง ดังนั้นคุณต้องการวัตถุในอนาคต แม้ว่าคุณจะไม่สนใจในผลของการทำงานเริ่ม
ในพื้นหลัง เพื่อให้สามารถแลกเปลี่ยนข้อมูลระหว่างสถานที่ที่เริ่มต้นและการควบคุมการทำงานและ
ส่งกลับวัตถุในอนาคต ทั้งดูที่เรียกว่ารัฐ ( ดูส่วนที่ 1 หน้า , 973 )
แน่นอนนอกจากนี้คุณยังสามารถ และมักจะใช้รถประกาศในอนาคต ( ผมอย่างชัดเจนต้องการ
แสดงชนิดของที่นี่ ) :
result1 อัตโนมัติ ( std : : การ ( func1 ) ) ;
2 เราจะเริ่ม func2() ในเบื้องหน้า นี้เป็นปกติแบบเรียกใช้ฟังก์ชันเพื่อให้
result2 โปรแกรมบล็อกที่นี่ : int = func2() ;
ดังนั้นถ้า func1() เรียบร้อยแล้วก็เริ่ม async() และไม่ได้จบลงไปแล้ว ตอนนี้เรามี
และ func1() func2() วิ่งขนาน
3 เราประมวลผลผลรวม นี่คือช่วงเวลาเมื่อเราต้องการผล func1() . จะได้รับมัน
เราเรียก get() สำหรับกลับมาในอนาคต :
1 ผล = result1 . get() result2 ;
ที่นี่ กับการเรียกของ get() หนึ่งในสามของสิ่งที่อาจเกิดขึ้น :
1 ถ้า func1() เริ่มด้วย async() ในหัวข้อที่แยกต่างหากและเสร็จแล้ว คุณได้รับผลทันที
.
2ถ้า func1() เริ่มต้นแต่ยังไม่เสร็จ แต่ get() บล็อกและรอสิ้นและผลผลผลิต
.
3 ถ้า func1() ยังไม่เริ่มเลย ก็ บี
การแปล กรุณารอสักครู่..
