คอมไพเลอร์ของฉันเพิกเฉยต่อสมาชิกคลาส thread_local ที่ไม่ได้ใช้ของฉันหรือไม่


10

ฉันต้องการลงทะเบียนกระทู้ในชั้นเรียนของฉันดังนั้นฉันตัดสินใจที่จะเพิ่มการตรวจสอบสำหรับthread_localคุณสมบัติ:

#include <iostream>
#include <thread>

class Foo {
 public:
  Foo() {
    std::cout << "Foo()" << std::endl;
  }
  ~Foo() {
    std::cout << "~Foo()" << std::endl;
  }
};

class Bar {
 public:
  Bar() {
    std::cout << "Bar()" << std::endl;
    //foo;
  }
  ~Bar() {
    std::cout << "~Bar()" << std::endl;
  }
 private:
  static thread_local Foo foo;
};

thread_local Foo Bar::foo;

void worker() {
  {
    std::cout << "enter block" << std::endl;
    Bar bar1;
    Bar bar2;
    std::cout << "exit block" << std::endl;
  }
}

int main() {
  std::thread t1(worker);
  std::thread t2(worker);
  t1.join();
  t2.join();
  std::cout << "thread died" << std::endl;
}

รหัสนั้นง่าย ฉันBarชั้นจะมีคงที่สมาชิกthread_local fooหากมีการสร้างสแตติกthread_local Foo fooหมายความว่าเธรดจะถูกสร้างขึ้น

แต่เมื่อฉันเรียกใช้รหัสไม่มีอะไรในFoo()ภาพพิมพ์และถ้าฉันลบความคิดเห็นในตัวBarสร้างของที่ใช้fooรหัสทำงานได้ดี

ฉันลองสิ่งนี้ใน GCC (7.4.0) และ Clang (6.0.0) และผลลัพธ์ก็เหมือนกัน ฉันเดาว่าคอมไพเลอร์พบว่าไม่ได้fooใช้และไม่สร้างอินสแตนซ์ ดังนั้น

  1. คอมไพเลอร์เพิกเฉยต่อstatic thread_localสมาชิกหรือไม่ ฉันจะแก้ไขข้อบกพร่องนี้ได้อย่างไร
  2. ถ้าเป็นเช่นนั้นเหตุใดstaticสมาชิกปกติจึงไม่มีปัญหานี้

คำตอบ:


9

ไม่มีปัญหากับการสังเกตของคุณ [basic.stc.static] / 2ห้ามกำจัดตัวแปรด้วยระยะเวลาการจัดเก็บแบบคงที่:

หากตัวแปรที่มีระยะเวลาการจัดเก็บแบบคงที่มีการเริ่มต้นหรือ destructor ที่มีผลข้างเคียงมันจะไม่ถูกกำจัดแม้ว่าจะดูเหมือนว่าไม่ได้ใช้งานยกเว้นว่าวัตถุคลาสหรือคัดลอก / ย้ายอาจถูกกำจัดตามที่ระบุใน [class.copy] .

ข้อ จำกัด นี้ไม่ปรากฏสำหรับระยะเวลาการเก็บข้อมูลอื่น ในความเป็นจริง[basic.stc.thread] / 2พูดว่า:

ตัวแปรที่มีระยะเวลาการเก็บด้ายจะต้องเริ่มต้นก่อนที่จะใช้งานครั้งแรกและถ้าสร้างจะถูกทำลายเมื่อออกจากกระทู้

สิ่งนี้ชี้ให้เห็นว่าไม่จำเป็นต้องสร้างตัวแปรที่มีระยะเวลาการเก็บด้ายจนกว่าจะมีการใช้


แต่ทำไมความคลาดเคลื่อนนี้?

สำหรับระยะเวลาการเก็บข้อมูลคงที่มีเพียงหนึ่งอินสแตนซ์ของตัวแปรต่อโปรแกรม ผลข้างเคียงของการก่อสร้างอาจมีความสำคัญ (ค่อนข้างเหมือนตัวสร้างโปรแกรมแบบกว้าง) ดังนั้นจึงต้องมีผลข้างเคียง

สำหรับช่วงเวลาที่เก็บโลคัลเธรดอย่างไรก็ตามมีปัญหา: อัลกอริทึมอาจเริ่มเธรดจำนวนมาก สำหรับเธรดเหล่านี้ส่วนใหญ่ตัวแปรจะไม่เกี่ยวข้องอย่างสมบูรณ์ มันคงจะเป็นเรื่องตลกถ้าห้องสมุดจำลองฟิสิกส์ภายนอกเรียกร้องstd::reduce(std::execution::par_unseq, first, last)ให้สร้างfooอินสแตนซ์จำนวนมากใช่มั้ย

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


1
โอเค ... ถ้ามาตรฐานพูดอย่างนั้นมันก็เป็นอย่างนั้น ... แต่มันไม่แปลกหรอกที่คณะกรรมการจะไม่พิจารณาผลข้างเคียงจากระยะเวลาการจัดเก็บแบบคงที่?
ravenisadesk

@reavenisadesk ดูคำตอบที่ปรับปรุงแล้ว
LF

1

ฉันพบข้อมูลนี้ใน "การจัดการเอลฟ์สำหรับการจัดเก็บในเธรดท้องถิ่น " ซึ่งสามารถพิสูจน์ได้ว่าคำตอบของ @LF

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

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