C ++ 11 ตัวแปร thread_local คงที่โดยอัตโนมัติหรือไม่


85

มีความแตกต่างระหว่างส่วนรหัสทั้งสองนี้หรือไม่:

void f() {
    thread_local vector<int> V;
    V.clear();
    ... // use V as a temporary variable
}

และ

void f() {
    static thread_local vector<int> V;
    V.clear();
    ... // use V as a temporary variable
}

Backstory: เดิมทีฉันมีเวกเตอร์ STATIC V (สำหรับการเก็บค่ากลางบางค่ามันจะถูกล้างทุกครั้งที่ฉันเข้าสู่ฟังก์ชัน) และโปรแกรมเธรดเดียว ฉันต้องการเปลี่ยนโปรแกรมให้เป็นโปรแกรมมัลติเธรดดังนั้นฉันต้องกำจัดตัวปรับคงที่นี้ออกไป ความคิดของฉันคือเปลี่ยนค่าคงที่ทั้งหมดให้เป็น thread_local และไม่ต้องกังวลกับสิ่งอื่นใด วิธีนี้สามารถย้อนกลับได้หรือไม่?


17
การมีthread_localตัวแปรท้องถิ่นไม่สมเหตุสมผลที่จะเริ่มต้นด้วย ... แต่ละเธรดมี call stack ของตัวเอง
Konrad Rudolph

1
ฟังก์ชัน C หลายฟังก์ชันถูกเขียนขึ้นเพื่อส่งคืนที่อยู่ของตัวแปรคงที่หรือทั่วโลก สิ่งนี้พบในภายหลังว่านำไปสู่ข้อบกพร่องที่คลุมเครือเมื่อใช้ในแอพแบบมัลติเธรด (เช่น errno, localtime) นอกจากนี้ในบางครั้งการป้องกันตัวแปรที่ใช้ร่วมกันด้วย mutex อาจเป็นอันตรายอย่างมากเมื่อมีการเรียกใช้ฟังก์ชันจากหลายเธรดหรือต้องส่งผ่านอ็อบเจกต์บริบทเธรดท่ามกลางอ็อบเจ็กต์และวิธีการเรียกจำนวนมากตัวแปรที่เป็นแบบโลคัลเพื่อแก้เธรด ปัญหาเหล่านี้และปัญหาอื่น ๆ
edwinc

3
@Konrad Rudolph ประกาศตัวแปรโลคัล แต่เพียงอย่างเดียวstaticแทนที่จะstatic thread_localไม่เริ่มต้นตัวแปรหนึ่งอินสแตนซ์สำหรับแต่ละเธรด
davide

1
@davide นั่นไม่ใช่ประเด็นทั้งของฉันหรือของ OP เราไม่ได้พูดถึงstaticvs static thread_localแต่เป็นเรื่องเกี่ยวกับautovs thread_localโดยใช้ความหมายก่อน C ++ 11 ของauto(เช่นที่เก็บข้อมูลอัตโนมัติ)
Konrad Rudolph

1
ดูวิธีกำหนดตัวแปรแบบคงที่ภายในเธรดโลคัลได้อย่างไร . หมายเหตุเกี่ยวกับทนายความภาษาฉบับย่อ ... การสนับสนุนของ Microsoft และ TLS มีการเปลี่ยนแปลงใน Vista ดูด้ายเก็บข้อมูลท้องถิ่น (TLS) การเปลี่ยนแปลงจะส่งผลกระทบต่อสิ่งต่างๆเช่น Singleton และอาจมีผลหรือไม่ก็ได้ หากคุณใช้โมเดลซอฟต์แวร์ abondware คุณอาจจะโอเค หากคุณยินดีที่จะสนับสนุนคอมไพเลอร์และแพลตฟอร์มหลายตัวคุณอาจต้องใส่ใจกับมัน
jww

คำตอบ:


94

ตามมาตรฐาน C ++

เมื่อ thread_local ถูกนำไปใช้กับตัวแปรของขอบเขตบล็อกสแตติกของ storage-class-specifier จะแสดงเป็นนัยถ้าไม่ปรากฏอย่างชัดเจน

ดังนั้นจึงหมายความว่าคำจำกัดความนี้

void f() {
    thread_local vector<int> V;
    V.clear();
    ... // use V as a temporary variable
}

เทียบเท่ากับ

void f() {
    static thread_local vector<int> V;
    V.clear();
    ... // use V as a temporary variable
}

อย่างไรก็ตามตัวแปรคงไม่เหมือนกับตัวแปร thread_local

1 ตัวแปรทั้งหมดที่ประกาศด้วยคีย์เวิร์ด thread_local มีระยะเวลาการจัดเก็บเธรด หน่วยเก็บข้อมูลสำหรับเอนทิตีเหล่านี้จะคงอยู่ตลอดระยะเวลาของเธรดที่สร้างขึ้น มีอ็อบเจ็กต์หรือการอ้างอิงที่แตกต่างกันสำหรับเธรดและการใช้ชื่อที่ประกาศอ้างถึงเอนทิตีที่เกี่ยวข้องกับเธรดปัจจุบัน

ในการแยกแยะตัวแปรเหล่านี้มาตรฐานจะแนะนำระยะเวลาการจัดเก็บเธรดเทอมใหม่พร้อมกับระยะเวลาการจัดเก็บแบบคงที่


1
staticและexternดังนั้นจึงไม่ได้หมายความอุปกรณ์เก็บข้อมูล แต่เพียงการเชื่อมโยงสำหรับตัวแปร thread_local แม้จะอยู่ในขอบเขตภายใน
Deduplicator

4
@Deduplicator ตัวแปรขอบเขตบล็อกไม่มีการเชื่อมโยง ดังนั้นเรซูเม่ของคุณผิด ตามที่ฉันเขียนในโพสต์พวกเขามีระยะเวลาการจัดเก็บเธรด ในความเป็นจริงมันเหมือนกับระยะเวลาการจัดเก็บคงที่ แต่ใช้กับแต่ละเธรด
Vlad จากมอสโก

1
หากคุณเพิ่มภายนอกแสดงว่าคุณกำลังทำการประกาศไม่ใช่คำจำกัดความ ดังนั้น?
Deduplicator

1
@Deduplicator ดังนั้นจึงหมายความว่าคำจำกัดความของตัวแปรขอบเขตบล็อกไม่มีการเชื่อมโยง
Vlad จากมอสโก

1
ฉันเพิ่งลองใน VS 2013 และมันขึ้นว่า "ตัวแปร TL ไม่สามารถเริ่มต้นแบบไดนามิกได้" ฉันงงงวย
v.oddou

19

ใช่ "thread-local storage" คล้ายกับ "global" (หรือ "static storage") มากเพียงแต่ว่าแทนที่จะเป็น "ระยะเวลาของโปรแกรมทั้งหมด" คุณมี "ระยะเวลาของเธรดทั้งหมด" ดังนั้นตัวแปร block-local thread-local จึงเริ่มต้นการควบคุมในครั้งแรกจะผ่านการประกาศ แต่แยกกันภายในแต่ละเธรดและจะถูกทำลายเมื่อเธรดสิ้นสุดลง


6

เมื่อใช้กับthread_local,staticเป็นนัยในบล็อกขอบเขต (ดู @ คำตอบของวลาด) requied สมาชิกชั้นเรียน; ฉันเดาว่าหมายถึงการเชื่อมโยงสำหรับขอบเขตเนมสเปซ

ต่อ 9.2 / 6:

ภายในนิยามคลาสจะไม่ประกาศสมาชิกด้วย thread_local storage-class-specifier เว้นแต่จะประกาศแบบคงที่

ในการตอบคำถามเดิม:

C ++ 11 ตัวแปร thread_local คงที่โดยอัตโนมัติหรือไม่

ไม่มีทางเลือกยกเว้นตัวแปรเนมสเปซขอบเขต

มีความแตกต่างระหว่างส่วนรหัสทั้งสองนี้หรือไม่:

ไม่


4

พื้นที่จัดเก็บในระบบเธรดเป็นแบบคงที่ แต่ทำงานค่อนข้างแตกต่างจากที่เก็บข้อมูลคงที่

เมื่อคุณประกาศตัวแปรคงที่จะมีตัวแปรหนึ่งอินสแตนซ์ ระบบคอมไพเลอร์ / รันไทม์รับประกันว่าระบบจะเริ่มต้นให้คุณในช่วงเวลาหนึ่งก่อนที่คุณจะใช้งานจริงโดยไม่ระบุว่าเมื่อใด (รายละเอียดบางส่วนไม่ได้ระบุไว้ที่นี่)

C ++ 11 รับประกันว่าการเริ่มต้นนี้จะปลอดภัยสำหรับเธรดอย่างไรก็ตามก่อนที่ C ++ 11 จะไม่รับประกันความปลอดภัยของเธรดนี้ ตัวอย่างเช่น

static X * pointer = new X;

อาจรั่วไหลของอินสแตนซ์ของ X หากเธรดมากกว่าหนึ่งรายการกดรหัสเริ่มต้นแบบคงที่ในเวลาเดียวกัน

เมื่อคุณประกาศตัวแปรเธรดในพื้นที่อาจมีหลายอินสแตนซ์ของตัวแปร คุณอาจคิดว่าพวกมันอยู่ในแผนที่ที่ทำดัชนีโดย thread-id นั่นหมายความว่าแต่ละเธรดจะเห็นสำเนาตัวแปรของตัวเอง

อีกครั้งหากตัวแปรถูกเตรียมใช้งานระบบคอมไพลเลอร์ / รันไทม์รับประกันว่าการเริ่มต้นนี้จะเกิดขึ้นก่อนที่จะใช้ข้อมูลและการเริ่มต้นจะเกิดขึ้นสำหรับแต่ละเธรดที่ใช้ตัวแปร คอมไพเลอร์ยังรับประกันว่าการเริ่มต้นจะปลอดภัยสำหรับเธรด

การรับประกันความปลอดภัยของเธรดหมายความว่าอาจมีโค้ดเบื้องหลังอยู่เล็กน้อยเพื่อให้ตัวแปรทำงานในแบบที่คุณคาดหวังโดยเฉพาะอย่างยิ่งเมื่อพิจารณาว่าคอมไพเลอร์ไม่มีทางรู้ล่วงหน้าว่าจะมีเธรดจำนวนเท่าใด มีอยู่ในโปรแกรมของคุณและจำนวนเหล่านี้จะสัมผัสกับตัวแปรท้องถิ่นของเธรด


@Etherealone: ​​น่าสนใจ ข้อมูลใดเป็นพิเศษ คุณสามารถให้ข้อมูลอ้างอิงได้หรือไม่?
Dale Wilson

1
stackoverflow.com/a/8102145/1576556 บทความ Wikipedia C ++ 11 กล่าวถึงถ้าฉันจำถูก
Etherealone

1
อย่างไรก็ตามวัตถุคงที่จะเริ่มต้นก่อนจากนั้นคัดลอกที่กำหนด ดังนั้นฉันจึงไม่ชัดเจนเล็กน้อยว่าการเริ่มต้นเธรดปลอดภัยรวมถึงนิพจน์ทั้งหมดหรือไม่ อาจเป็นเพราะไม่เช่นนั้นจะไม่ถือว่าปลอดภัย
Etherealone
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.