C ++ 0x ไม่มี semaphores? จะซิงโครไนซ์เธรดได้อย่างไร?


135

C ++ 0x จะมาโดยไม่มี semaphores จริงหรือ? มีคำถามบางอย่างเกี่ยวกับ Stack Overflow เกี่ยวกับการใช้ semaphores อยู่แล้ว ฉันใช้มัน (posix semaphores) ตลอดเวลาเพื่อให้เธรดรอเหตุการณ์บางอย่างในเธรดอื่น:

void thread0(...)
{
  doSomething0();

  event1.wait();

  ...
}

void thread1(...)
{
  doSomething1();

  event1.post();

  ...
}

ถ้าฉันจะทำแบบนั้นกับ mutex:

void thread0(...)
{
  doSomething0();

  event1.lock(); event1.unlock();

  ...
}

void thread1(...)
{
  event1.lock();

  doSomethingth1();

  event1.unlock();

  ...
}

ปัญหา: มันน่าเกลียดและไม่รับประกันว่า thread1 จะล็อก mutex ก่อน (เนื่องจากเธรดเดียวกันควรล็อกและปลดล็อก mutex คุณจึงไม่สามารถล็อก event1 ก่อนที่ thread0 และ thread1 จะเริ่ม)

ดังนั้นเนื่องจากการบูสต์ไม่มีเซมาโฟเรสด้วยเช่นกันวิธีที่ง่ายที่สุดในการบรรลุสิ่งข้างต้นคืออะไร?


อาจจะใช้เงื่อนไข mutex และ std :: สัญญาและ std :: future?
Yves

คำตอบ:


181

คุณสามารถสร้างได้อย่างง่ายดายจาก mutex และตัวแปรเงื่อนไข:

#include <mutex>
#include <condition_variable>

class semaphore
{
private:
    std::mutex mutex_;
    std::condition_variable condition_;
    unsigned long count_ = 0; // Initialized as locked.

public:
    void notify() {
        std::lock_guard<decltype(mutex_)> lock(mutex_);
        ++count_;
        condition_.notify_one();
    }

    void wait() {
        std::unique_lock<decltype(mutex_)> lock(mutex_);
        while(!count_) // Handle spurious wake-ups.
            condition_.wait(lock);
        --count_;
    }

    bool try_wait() {
        std::lock_guard<decltype(mutex_)> lock(mutex_);
        if(count_) {
            --count_;
            return true;
        }
        return false;
    }
};

100
ควรมีคนส่งข้อเสนอไปยังคณะกรรมการมาตรฐาน

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

32
มันถูกแยกออกจาก Boost โดยเจตนาเนื่องจากเซมาฟอร์เป็นเชือกมากเกินไปสำหรับโปรแกรมเมอร์ที่จะแขวนคอตัวเอง ตัวแปรเงื่อนไขที่ควรจะจัดการได้มากกว่า ฉันเห็นประเด็นของพวกเขา แต่รู้สึกได้รับการอุปถัมภ์เล็กน้อย ฉันคิดว่าตรรกะเดียวกันนี้ใช้กับ C ++ 11 - โปรแกรมเมอร์คาดว่าจะเขียนโปรแกรมในลักษณะที่ "เป็นธรรมชาติ" ใช้ condvars หรือเทคนิคการซิงโครไนซ์ที่ได้รับการอนุมัติอื่น ๆ การจัดหาเซมาฟอร์จะดำเนินการกับสิ่งนั้นไม่ว่าจะใช้งานบน condvar หรือโดยกำเนิด
Steve Jessop

5
หมายเหตุ - ดูen.wikipedia.org/wiki/Spurious_wakeupสำหรับเหตุผลเบื้องหลังการwhile(!count_)วนซ้ำ
Dan Nissenbaum

3
@ Maxim ฉันขอโทษฉันคิดว่าคุณไม่ถูกต้อง sem_wait และ sem_post เฉพาะ syscall ในการโต้แย้งเช่นกัน (ตรวจสอบsourceware.org/git/?p=glibc.git;a=blob;f=nptl/sem_wait.c ) ดังนั้นโค้ดที่นี่จึงลงเอยด้วยการทำซ้ำการใช้งาน libc โดยอาจมีข้อบกพร่อง หากคุณต้องการพกพาในระบบใด ๆ อาจเป็นวิธีแก้ปัญหา แต่ถ้าคุณต้องการเพียงความเข้ากันได้ของ Posix ให้ใช้ Posix semaphore
xryl669

107

จากคำตอบของ Maxim Yegorushkinฉันพยายามสร้างตัวอย่างในรูปแบบ C ++ 11

#include <mutex>
#include <condition_variable>

class Semaphore {
public:
    Semaphore (int count_ = 0)
        : count(count_) {}

    inline void notify()
    {
        std::unique_lock<std::mutex> lock(mtx);
        count++;
        cv.notify_one();
    }

    inline void wait()
    {
        std::unique_lock<std::mutex> lock(mtx);

        while(count == 0){
            cv.wait(lock);
        }
        count--;
    }

private:
    std::mutex mtx;
    std::condition_variable cv;
    int count;
};

35
คุณสามารถรอ () สามซับ:cv.wait(lck, [this]() { return count > 0; });
Domi

2
การเพิ่มคลาสอื่นในจิตวิญญาณของ lock_guard ก็มีประโยชน์เช่นกัน ในแฟชั่น RAII ตัวสร้างซึ่งใช้สัญญาณเป็นข้อมูลอ้างอิงเรียกการเรียก wait () ของเซมาฟอร์และผู้ทำลายจะเรียกการโทรแจ้งเตือน () สิ่งนี้ป้องกันข้อยกเว้นจากความล้มเหลวในการปล่อยสัญญาณ
Jim Hunziker

ไม่มีล็อคตายถ้าพูด N เธรดที่เรียกว่า wait () และนับ == 0 แล้ว cv.notify_one (); ไม่เคยถูกเรียกเนื่องจาก mtx ยังไม่เปิดตัว?
Marcello

1
@Marcello กระทู้รอไม่ได้ล็อค จุดรวมของตัวแปรเงื่อนไขคือการดำเนินการ "ปลดล็อกและรอ" แบบปรมาณู
David Schwartz

4
คุณควรปลดล็อกก่อนโทรแจ้งเตือน () เพื่อหลีกเลี่ยงการปิดกั้นการปลุกในทันที ... ดูที่นี่: en.cppreference.com/w/cpp/thread/condition_variable/notify_all
kylefinn

38

ฉันตัดสินใจที่จะเขียนเซมาฟอร์ C ++ 11 ที่แข็งแกร่งที่สุด / ทั่วไปที่ฉันทำได้ในรูปแบบของมาตรฐานให้มากที่สุดเท่าที่จะทำได้ (โปรดทราบusing semaphore = ...ว่าโดยปกติคุณจะใช้ชื่อที่semaphoreคล้ายกับการใช้stringnot โดยปกติbasic_string):

template <typename Mutex, typename CondVar>
class basic_semaphore {
public:
    using native_handle_type = typename CondVar::native_handle_type;

    explicit basic_semaphore(size_t count = 0);
    basic_semaphore(const basic_semaphore&) = delete;
    basic_semaphore(basic_semaphore&&) = delete;
    basic_semaphore& operator=(const basic_semaphore&) = delete;
    basic_semaphore& operator=(basic_semaphore&&) = delete;

    void notify();
    void wait();
    bool try_wait();
    template<class Rep, class Period>
    bool wait_for(const std::chrono::duration<Rep, Period>& d);
    template<class Clock, class Duration>
    bool wait_until(const std::chrono::time_point<Clock, Duration>& t);

    native_handle_type native_handle();

private:
    Mutex   mMutex;
    CondVar mCv;
    size_t  mCount;
};

using semaphore = basic_semaphore<std::mutex, std::condition_variable>;

template <typename Mutex, typename CondVar>
basic_semaphore<Mutex, CondVar>::basic_semaphore(size_t count)
    : mCount{count}
{}

template <typename Mutex, typename CondVar>
void basic_semaphore<Mutex, CondVar>::notify() {
    std::lock_guard<Mutex> lock{mMutex};
    ++mCount;
    mCv.notify_one();
}

template <typename Mutex, typename CondVar>
void basic_semaphore<Mutex, CondVar>::wait() {
    std::unique_lock<Mutex> lock{mMutex};
    mCv.wait(lock, [&]{ return mCount > 0; });
    --mCount;
}

template <typename Mutex, typename CondVar>
bool basic_semaphore<Mutex, CondVar>::try_wait() {
    std::lock_guard<Mutex> lock{mMutex};

    if (mCount > 0) {
        --mCount;
        return true;
    }

    return false;
}

template <typename Mutex, typename CondVar>
template<class Rep, class Period>
bool basic_semaphore<Mutex, CondVar>::wait_for(const std::chrono::duration<Rep, Period>& d) {
    std::unique_lock<Mutex> lock{mMutex};
    auto finished = mCv.wait_for(lock, d, [&]{ return mCount > 0; });

    if (finished)
        --mCount;

    return finished;
}

template <typename Mutex, typename CondVar>
template<class Clock, class Duration>
bool basic_semaphore<Mutex, CondVar>::wait_until(const std::chrono::time_point<Clock, Duration>& t) {
    std::unique_lock<Mutex> lock{mMutex};
    auto finished = mCv.wait_until(lock, t, [&]{ return mCount > 0; });

    if (finished)
        --mCount;

    return finished;
}

template <typename Mutex, typename CondVar>
typename basic_semaphore<Mutex, CondVar>::native_handle_type basic_semaphore<Mutex, CondVar>::native_handle() {
    return mCv.native_handle();
}

วิธีนี้ใช้ได้ผลกับการแก้ไขเล็กน้อย wait_forและwait_untilวิธีการโทรด้วยกริยากลับค่าบูลีน (ไม่ `มาตรฐาน :: cv_status)
jdknight

ขอโทษที่เลือกช้าในเกม std::size_tจะไม่ได้ลงนามเพื่อ decrementing มันต่ำกว่าศูนย์เป็น UB >= 0และมันจะเป็น IMHO countควรเป็นintไฟล์.
Richard Hodges

3
@RichardHodges ไม่มีทางที่จะลดลงต่ำกว่าศูนย์ดังนั้นจึงไม่มีปัญหาและการนับลบบนสัญญาณหมายถึงอะไร? นั่นไม่ได้ทำให้รู้สึกถึง IMO
David

1
@ เดวิดเกิดอะไรขึ้นถ้าเธรดต้องรอให้คนอื่นมากระตุ้นสิ่งต่างๆ? ตัวอย่างเช่นเธรดผู้อ่าน 1 เธรดเพื่อรอ 4 เธรดฉันจะเรียกตัวสร้างเซมาฟอร์ด้วย -3 เพื่อให้เธรดผู้อ่านรอจนกว่าเธรดอื่น ๆ ทั้งหมดที่โพสต์ ฉันเดาว่ามีวิธีอื่นในการทำเช่นนั้น แต่มันไม่สมเหตุสมผลเหรอ? ฉันคิดว่ามันเป็นคำถามที่ OP ถาม แต่มี "thread1" มากกว่า
jmmut

3
@RichardHodges เป็นคนอวดรู้มากการลดประเภทจำนวนเต็มที่ไม่ได้ลงนามด้านล่าง 0 ไม่ใช่ UB
jcai

15

ในความสอดคล้องกับเซมาโฟเรส posix ฉันจะเพิ่ม

class semaphore
{
    ...
    bool trywait()
    {
        boost::mutex::scoped_lock lock(mutex_);
        if(count_)
        {
            --count_;
            return true;
        }
        else
        {
            return false;
        }
    }
};

และฉันชอบใช้กลไกการซิงโครไนซ์ในระดับนามธรรมที่สะดวกมากกว่าการคัดลอกวางเวอร์ชันที่ต่อเข้าด้วยกันโดยใช้ตัวดำเนินการพื้นฐานมากกว่าเสมอ


9

คุณยังสามารถตรวจสอบcpp11-on-multicoreได้ซึ่งมีการใช้งานสัญญาณแบบพกพาและเหมาะสมที่สุด

ที่เก็บยังมีสารพัดเธรดอื่น ๆ ที่เสริมเธรด c ++ 11


8

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

มีตัวอย่างสั้น ๆในไลบรารี boost :: thread ที่คุณสามารถคัดลอกได้มากที่สุด (C ++ 0x และ boost thread libs คล้ายกันมาก)


เงื่อนไขส่งสัญญาณถึงเธรดที่รอเท่านั้นหรือไม่? ดังนั้นหากไม่มี thread0 รอเมื่อ thread1 ส่งสัญญาณมันจะถูกบล็อกในภายหลัง? บวก: ฉันไม่ต้องการตัวล็อคเพิ่มเติมที่มาพร้อมกับเงื่อนไข - มันอยู่เหนือศีรษะ
tauran

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

2
คุณสามารถจำลองเซมาฟอร์ได้ด้วยวิธีนี้: เริ่มต้นตัวแปรด้วยค่าที่คุณจะให้เซมาฟอร์จากนั้นwait()จะแปลเป็น "ล็อกตรวจสอบการนับว่าการลดลงที่ไม่ใช่ศูนย์และดำเนินการต่อหากศูนย์รอเงื่อนไข" ในขณะที่postจะเป็น "ล็อก ตัวนับส่วนเพิ่มให้สัญญาณว่าเป็น 0 "
David Rodríguez - dribeas

ใช่ฟังดูดี ฉันสงสัยว่าเซมาโฟเรส posix ถูกนำไปใช้ในลักษณะเดียวกันหรือไม่
tauran

@tauran: ฉันไม่รู้แน่ชัด (และอาจขึ้นอยู่กับ Posix OS ตัวไหน) แต่ฉันคิดว่าไม่น่าเป็นไปได้ Semaphores ตามเนื้อผ้าเป็นแบบดั้งเดิมของการซิงโครไนซ์ "ระดับต่ำกว่า" มากกว่า mutexes และตัวแปรเงื่อนไขและโดยหลักการแล้วสามารถทำให้มีประสิทธิภาพมากกว่าที่จะเป็นหากนำไปใช้บน condvar ดังนั้นสิ่งที่เป็นไปได้มากกว่าในระบบปฏิบัติการที่กำหนดก็คือไพรมารีซิงก์ระดับผู้ใช้ทั้งหมดถูกสร้างขึ้นจากเครื่องมือทั่วไปบางอย่างที่โต้ตอบกับตัวกำหนดตารางเวลา
Steve Jessop

5

C ++ 20 ในที่สุดก็จะมีสัญญาณ std::counting_semaphore<max_count>-

สิ่งเหล่านี้จะมี (อย่างน้อย) วิธีการต่อไปนี้:

  • acquire() (การปิดกั้น)
  • try_acquire() (ไม่ปิดกั้นส่งกลับทันที)
  • try_acquire_for() (ไม่ปิดกั้นใช้เวลาสักครู่)
  • try_acquire_until() (ไม่ใช่การปิดกั้นใช้เวลาสักครู่ในการหยุดพยายาม)
  • release()

สิ่งนี้ยังไม่ปรากฏใน cppreference แต่คุณสามารถอ่านสไลด์นำเสนอ CppCon 2019 เหล่านี้หรือดูวิดีโอได้ นอกจากนี้ยังมีข้อเสนออย่างเป็นทางการP0514R4แต่ฉันไม่แน่ใจว่าเป็นเวอร์ชันล่าสุด


3

นอกจากนี้ยังสามารถเป็นเครื่องห่อสัญญาณ RAII ที่มีประโยชน์ในเธรด:

class ScopedSemaphore
{
public:
    explicit ScopedSemaphore(Semaphore& sem) : m_Semaphore(sem) { m_Semaphore.Wait(); }
    ScopedSemaphore(const ScopedSemaphore&) = delete;
    ~ScopedSemaphore() { m_Semaphore.Notify(); }

   ScopedSemaphore& operator=(const ScopedSemaphore&) = delete;

private:
    Semaphore& m_Semaphore;
};

ตัวอย่างการใช้งานในแอปมัลติเธรด:

boost::ptr_vector<std::thread> threads;
Semaphore semaphore;

for (...)
{
    ...
    auto t = new std::thread([..., &semaphore]
    {
        ScopedSemaphore scopedSemaphore(semaphore);
        ...
    }
    );
    threads.push_back(t);
}

for (auto& t : threads)
    t.join();

2

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

ฉันทำสิ่งนี้ได้อย่างไรฉันสร้างโครงสร้าง:

struct UpdateLock
{
    typedef std::shared_ptr< UpdateLock > ptr;
};

ลูกค้าแต่ละรายจะมีสมาชิกดังต่อไปนี้:

UpdateLock::ptr m_myLock;

จากนั้นโฮสต์จะมีสมาชิกจุดอ่อนสำหรับการผูกขาดและรายชื่อจุดอ่อนสำหรับการล็อกที่ไม่ผูกขาด:

std::weak_ptr< UpdateLock > m_exclusiveLock;
std::list< std::weak_ptr< UpdateLock > > m_locks;

มีฟังก์ชั่นสำหรับเปิดใช้งานการล็อกและอีกฟังก์ชันหนึ่งเพื่อตรวจสอบว่าโฮสต์ถูกล็อกหรือไม่:

UpdateLock::ptr LockUpdate( bool exclusive );       
bool IsUpdateLocked( bool exclusive ) const;

ฉันทดสอบการล็อกใน LockUpdate, IsUpdateLocked และเป็นระยะในกิจวัตรการอัปเดตของโฮสต์ การทดสอบการล็อกนั้นง่ายมากเพียงแค่ตรวจสอบว่าจุดอ่อน_ptrหมดอายุหรือไม่และลบส่วนที่หมดอายุออกจากรายการ m_locks (ฉันทำสิ่งนี้ในระหว่างการอัปเดตโฮสต์เท่านั้น) ฉันสามารถตรวจสอบว่ารายการว่างเปล่าหรือไม่ ในเวลาเดียวกันฉันจะได้รับการปลดล็อกอัตโนมัติเมื่อไคลเอนต์รีเซ็ต shared_ptr ที่พวกเขาแขวนอยู่ซึ่งจะเกิดขึ้นเมื่อไคลเอนต์ถูกทำลายโดยอัตโนมัติ

ผลกระทบทั้งหมดคือเนื่องจากไคลเอนต์แทบไม่ต้องการความพิเศษ (โดยทั่วไปสงวนไว้สำหรับการเพิ่มและการลบเท่านั้น) โดยส่วนใหญ่แล้วการร้องขอเพื่อ LockUpdate (เท็จ) กล่าวคือไม่ผูกขาด แต่จะประสบความสำเร็จตราบเท่าที่ (! m_exclusiveLock) และ LockUpdate (true) ซึ่งเป็นคำขอสำหรับการผูกขาดจะสำเร็จก็ต่อเมื่อทั้ง (! m_exclusiveLock) และ (m_locks.empty ())

สามารถเพิ่มคิวเพื่อลดการล็อกระหว่างการล็อกแบบพิเศษและแบบไม่ผูกขาดได้อย่างไรก็ตามจนถึงขณะนี้ฉันไม่มีการชนกันดังนั้นฉันจึงตั้งใจที่จะรอจนกว่าสิ่งนั้นจะเกิดขึ้นเพื่อเพิ่มโซลูชัน (ส่วนใหญ่ฉันจึงมีเงื่อนไขการทดสอบในโลกแห่งความเป็นจริง)

จนถึงตอนนี้มันทำงานได้ดีสำหรับความต้องการของฉัน ฉันสามารถจินตนาการได้ถึงความจำเป็นในการขยายสิ่งนี้และปัญหาบางอย่างที่อาจเกิดขึ้นจากการใช้งานแบบขยายอย่างไรก็ตามสิ่งนี้ใช้งานได้รวดเร็วและต้องการโค้ดที่กำหนดเองเพียงเล็กน้อย


-4

ในกรณีที่มีคนสนใจรุ่นอะตอมนี่คือการนำไปใช้งาน คาดว่าประสิทธิภาพจะดีกว่าเวอร์ชันตัวแปร mutex & condition

class semaphore_atomic
{
public:
    void notify() {
        count_.fetch_add(1, std::memory_order_release);
    }

    void wait() {
        while (true) {
            int count = count_.load(std::memory_order_relaxed);
            if (count > 0) {
                if (count_.compare_exchange_weak(count, count-1, std::memory_order_acq_rel, std::memory_order_relaxed)) {
                    break;
                }
            }
        }
    }

    bool try_wait() {
        int count = count_.load(std::memory_order_relaxed);
        if (count > 0) {
            if (count_.compare_exchange_strong(count, count-1, std::memory_order_acq_rel, std::memory_order_relaxed)) {
                return true;
            }
        }
        return false;
    }
private:
    std::atomic_int count_{0};
};

4
ผมจะคาดหวังผลการดำเนินงานที่จะมากแย่ลง รหัสนี้ทำให้เกิดความผิดพลาดเกือบทุกประการ เป็นเพียงตัวอย่างที่ชัดเจนที่สุดสมมติว่าwaitโค้ดต้องวนซ้ำหลายครั้ง เมื่อปลดบล็อกในที่สุดก็จะใช้แม่ของกิ่งก้านที่คาดเดาผิดทั้งหมดเนื่องจากการทำนายลูปของ CPU จะทำนายว่ามันจะวนซ้ำอีกครั้ง ฉันสามารถแสดงรายการปัญหาอื่น ๆ อีกมากมายเกี่ยวกับรหัสนี้
David Schwartz

1
นี่คือตัวฆ่าประสิทธิภาพที่ชัดเจนอีกตัวหนึ่ง: waitลูปจะใช้ทรัพยากร CPU microexecution เมื่อมันหมุน สมมติว่ามันอยู่ในคอร์กายภาพเดียวกันกับเธรดที่ควรจะnotifyเป็นมันจะทำให้เธรดนั้นช้าลงอย่างมาก
David Schwartz

1
และนี่เป็นเพียงหนึ่งเดียว: บนซีพียู x86 (ซีพียูที่ได้รับความนิยมมากที่สุดในปัจจุบัน) การดำเนินการ Compare_exchange_weak เป็นการดำเนินการเขียนเสมอแม้ว่าจะล้มเหลว (จะเขียนกลับค่าเดิมที่อ่านได้หากการเปรียบเทียบล้มเหลว) สมมติว่าคอร์สองคอร์อยู่ในwaitลูปสำหรับเซมาฟอร์เดียวกัน ทั้งคู่กำลังเขียนด้วยความเร็วเต็มที่ไปยังบรรทัดแคชเดียวกันซึ่งอาจทำให้คอร์อื่น ๆ ช้าลงในการรวบรวมข้อมูลโดยการทำให้บัสอินเตอร์คอร์อิ่มตัว
David Schwartz

@DavidSchwartz ดีใจที่ได้เห็นความคิดเห็นของคุณ ไม่แน่ใจว่าเข้าใจส่วน "... การทำนายลูปของ CPU ... " เห็นด้วยข้อที่ 2 เห็นได้ชัดว่ากรณีที่ 3 ของคุณสามารถเกิดขึ้นได้ แต่เมื่อเปรียบเทียบกับ mutex ซึ่งทำให้โหมดผู้ใช้เป็นสวิตช์โหมดเคอร์เนลและการเรียกระบบการซิงค์ระหว่างคอร์ไม่ได้แย่ลง
Jeffery

1
ไม่มีสิ่งที่เรียกว่าสัญญาณฟรีล็อค แนวคิดทั้งหมดของการล็อคฟรีไม่ใช่การเขียนโค้ดโดยไม่ใช้ mutexes แต่เขียนโค้ดโดยที่เธรดไม่เคยบล็อก ในกรณีนี้สาระสำคัญของเซมาฟอร์คือการบล็อกเธรดที่เรียกใช้ฟังก์ชัน wait ()!
Carlo Wood
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.