সি ++ 0x এর কোনও সেমোফোর নেই? কীভাবে থ্রেডগুলি সিঙ্ক্রোনাইজ করবেন?


135

এটি কি সত্য যে সি ++ 0x سیمফোরগুলি ছাড়াই আসবে? স্টেম ওভারফ্লোতে ইতিমধ্যে সেমোফোরগুলির ব্যবহার সম্পর্কে কিছু প্রশ্ন রয়েছে। আমি একটি থ্রেড অন্য থ্রেডে কিছু ইভেন্টের জন্য অপেক্ষা করতে দিতে সমস্ত সময় তাদের (পসিক্স সেমোফোরস) ব্যবহার করি:

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

  event1.wait();

  ...
}

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

  event1.post();

  ...
}

আমি যদি একটি মিটেক্স দিয়ে এটি করতাম:

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

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

  ...
}

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

  doSomethingth1();

  event1.unlock();

  ...
}

সমস্যা: এটি কুরুচিপূর্ণ এবং এটি গ্যারান্টিযুক্ত নয় যে থ্রেড 1 প্রথমে মিউটেক্সকে লক করে দেয় (একই থ্রেডটি একটি মুটেক্সকে লক এবং আনলক করা উচিত, আপনি থ্রেড 0 এবং থ্রেড 1 শুরুর আগে ইভেন্ট 1 লক করতে পারবেন না)।

সুতরাং যেহেতু বুস্টেও সেমফোর নেই, উপরেরটি অর্জনের সহজ উপায় কী?


শর্তটি মুটিেক্স এবং এসটিডি :: প্রতিশ্রুতি এবং এসটিডি :: ভবিষ্যতটি ব্যবহার করতে পারেন?
ইয়ভেস

উত্তর:


180

আপনি সহজেই একটি মুটেক্স এবং শর্ত পরিবর্তনশীল থেকে একটি তৈরি করতে পারেন:

#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;
    }
};

96
কারও কারও কাছে স্ট্যান্ডার্ড

7
এখানে একটি মন্তব্য যা আমাকে প্রথমে বিস্মিত করেছিল তা হ'ল লক ইন ওয়েট, কেউ জিজ্ঞাসা করতে পারে যে লকটি যদি অপেক্ষা করে রাখা হয় তবে কোনও থ্রেড কীভাবে অতীতকে জানাতে পারে? কিছুটা অস্পষ্টভাবে নথিভুক্ত উত্তরটি হ'ল

31
এটি বুস্ট থেকে ইচ্ছাকৃতভাবে বাদ দেওয়া হয়েছিল যে ভিত্তিতে প্রোগ্রামারদের জন্য নিজেকে ফাঁসানোর জন্য একটি সেমফোর খুব বেশি দড়ি। কন্ডিশনের ভেরিয়েবলগুলি সম্ভবত পরিচালনা করা যায়। আমি তাদের বক্তব্য দেখতে কিন্তু কিছুটা পৃষ্ঠপোষকতা বোধ করছি। আমি ধরে নিয়েছি যে একই যুক্তি সি ++ 11-র ক্ষেত্রে প্রযোজ্য - প্রোগ্রামাররা তাদের প্রোগ্রামগুলি এমনভাবে লিখবেন বলে আশা করা হয় যে "প্রাকৃতিকভাবে" কনডওয়ার বা অন্যান্য অনুমোদিত সিঙ্ক্রোনাইজেশন কৌশল ব্যবহার করে। কনডভরের শীর্ষে বা দেশীয়ভাবে প্রয়োগ করা হোক না কেন সেমফোর সরবরাহ করা তার বিরুদ্ধে চলে run
স্টিভ জেসোপ

5
দ্রষ্টব্য - লুপের পিছনে যুক্তি দেখানোর জন্য en.wikedia.org/wiki/Spurious_wakeup দেখুন while(!count_)
ড্যান নিসেনবাউম

3
@ ম্যাক্সিম আমি দুঃখিত, আমি মনে করি না আপনি ঠিক আছেন। সেম_ওয়েট এবং সেম_পোস্ট কেবলমাত্র বিতর্ককে কেন্দ্র করেই সিস্কেল করুন ( উত্স পরীক্ষা করুন সোর্স.আর.আর.গিট /?p=glibc.git ;a=blob ;f=nptl/sem_wait.c ) সুতরাং এখানে কোডটি সম্ভাব্য বাগ সহ libc প্রয়োগের নকল করে। আপনি যদি কোনও সিস্টেমে বহনযোগ্যতার উদ্দেশ্যে থাকেন তবে এটি সমাধান হতে পারে তবে আপনার যদি কেবল পিক্সিক্সের সামঞ্জস্যতা প্রয়োজন হয় তবে পিক্সিক্স সেমফোরটি ব্যবহার করুন।
xryl669

107

ম্যাক্সিম ইয়েগুরুশকিনের উত্তরের ভিত্তিতে , আমি সি ++ 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;
};

34
আপনি অপেক্ষা করতে পারেন () এছাড়াও একটি থ্রি-লাইনার:cv.wait(lck, [this]() { return count > 0; });
ডমি

2
লক_গার্ডের স্পিরিটে অন্য শ্রেণি যুক্ত করাও সহায়ক। RAII ফ্যাশনে, কনস্ট্রাক্টর, যা সেমফোরকে রেফারেন্স হিসাবে গ্রহণ করে, সেমফোরের ওয়েট () কলকে ডাস্ট্রাক্টর বলে এবং তার নোটিফ () কল দেয়। এটি ব্যতিক্রমগুলি সামোহর ছেড়ে দিতে ব্যর্থ হতে বাধা দেয়।
জিম হুনজিকার

সেখানে কোনও মৃত-লক নেই, যদি বলুন এন থ্রেডগুলি অপেক্ষা () এবং গণনা == 0 বলে, তবে cv.notify_one (); কখনও বলা হয় না, যেহেতু এমটিএক্স প্রকাশিত হয়নি?
মার্সেলো

1
@ মার্সেলো ওয়েটিং থ্রেডগুলি লকটি ধরে না। শর্ত ভেরিয়েবলগুলির সম্পূর্ণ বিন্দুটি একটি পারমাণবিক "আনলক এবং অপেক্ষা" অপারেশন সরবরাহ করা।
ডেভিড শোয়ার্জ

3
তাত্ক্ষণিকভাবে জাগানো আটকাতে এড়াতে আপনার notif_one () কল করার আগে লকটি প্রকাশ করা উচিত ... এখানে দেখুন: en.cppreferences.com/w/cpp/thread/condition_variable/notify_all
kylefinn

38

আমি সিদ্ধান্ত নিয়েছি যে সবচেয়ে শক্তিশালী / জেনেরিক সি ++ ১১ টি সেমফোর আমি স্ট্যান্ডার্ডের মতো যতটা পারি using semaphore = ...লিখতে পারি (দ্রষ্টব্য , আপনি সাধারণত সাধারণত নামটি semaphoreব্যবহার stringনা করে অনুরূপ ব্যবহার করবেন 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স্বাক্ষরযুক্ত নয় সুতরাং এটি শূন্যের নীচে হ্রাস হ'ল ইউবি, এবং এটি সর্বদা থাকবে >= 0। আইএমএইচও countহওয়া উচিত একটি int
রিচার্ড হেজস

3
@ রিচার্ডহজস শূন্যের নিচে হ্রাস পাওয়ার কোনও উপায় নেই যাতে কোনও সমস্যা নেই এবং একটি সেমফোরের নেতিবাচক গণনা বলতে কী বোঝায়? এটি এমনকি আইএমও করে তোলে না।
ডেভিড

1
@ ডেভিড কী হবে যদি কোনও থ্রেড অন্যদের জিনিস উদ্ভাবনের জন্য অপেক্ষা করতে থাকে? উদাহরণস্বরূপ, 1 টি পাঠক থ্রেড 4 টি থ্রেডের জন্য অপেক্ষা করুন, আমি পাঠক থ্রেডটি অপেক্ষারত অন্য সমস্ত থ্রেডের জন্য একটি পোস্ট তৈরি করার জন্য -3 দিয়ে সেমফোর কনস্ট্রাক্টরকে কল করব। আমি অনুমান করি যে এটি করার অন্যান্য উপায় আছে তবে এটি কি যুক্তিসঙ্গত নয়? আমি মনে করি এটি আসলে প্রশ্নটি ওপি জিজ্ঞাসা করছে তবে আরও "থ্রেড 1" এর সাথে।
jmmut

2
@ রিচার্ডহজস খুব প্যাড্যান্টিক হতে হবে, 0 এর নিচে স্বাক্ষরযুক্ত স্বাক্ষর পূর্ণসংখ্যা টাইপ হ্রাস করা ইউবি নয়।
jcai

15

পোস্টিক্স সেমোফোরগুলির সাথে একযোগে, আমি যুক্ত করব

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

এবং আমি আরও বেসিক অপারেটরগুলি ব্যবহার করে একটি সেলাইযুক্ত-একসাথে সংস্করণ আটকানোর অনুলিপি না করে বরং বিমূর্ততার একটি সুবিধাজনক পর্যায়ে একটি সিঙ্ক্রোনাইজেশন প্রক্রিয়া ব্যবহার করা পছন্দ করি।


9

আপনি সিপিপি 11-অন-মাল্টিকোরও পরীক্ষা করে দেখতে পারেন - এটিতে একটি বহনযোগ্য এবং সর্বোত্তম সেমফোর বাস্তবায়ন রয়েছে।

সংগ্রহস্থলে অন্যান্য থ্রেডিং গুডিও রয়েছে যা সি ++ 11 থ্রেডিংয়ের পরিপূরক।


8

আপনি মিটেক্স এবং শর্ত ভেরিয়েবলগুলির সাথে কাজ করতে পারেন। আপনি মুটেক্সের সাথে একচেটিয়া অ্যাক্সেস অর্জন করেছেন, আপনি চালিয়ে যেতে চান কিনা বা অন্য প্রান্তের জন্য অপেক্ষা করতে হবে কিনা তা পরীক্ষা করে দেখুন। আপনার যদি অপেক্ষা করতে হয় তবে আপনি একটি শর্তে অপেক্ষা করুন। যখন অন্য থ্রেড নির্ধারণ করে যে আপনি চালিয়ে যেতে পারেন, এটি শর্তটিকে সংকেত দেয়।

বুস্ট :: থ্রেড লাইব্রেরিতে একটি সংক্ষিপ্ত উদাহরণ রয়েছে যা আপনি সম্ভবত অনুলিপি করতে পারেন (সি ++ 0 এক্স এবং বুস্ট থ্রেডের লিবগুলি খুব মিল)।


শর্ত সংকেত শুধুমাত্র অপেক্ষার থ্রেডগুলিতে, না? সুতরাং থ্রেড 0 যদি অপেক্ষা না করে থ্রেড 1 সংকেতগুলি এটি পরে অবরুদ্ধ করা হবে? প্লাস: শর্তের সাথে আসা অতিরিক্ত লকটির দরকার নেই - এটি ওভারহেড।
tauran

হ্যাঁ, শর্তটি কেবলমাত্র অপেক্ষার থ্রেডের সংকেত দেয়। সাধারণ প্যাটার্নটির সাথে রাষ্ট্রের সাথে একটি পরিবর্তনশীল এবং যদি আপনাকে অপেক্ষা করতে হয় তবে একটি শর্ত থাকে। কোনও প্রযোজক / ভোক্তার কথা চিন্তা করুন, বাফারের আইটেমগুলির মধ্যে একটি গণনা থাকবে, প্রযোজক তালা দিয়ে, উপাদানটি যুক্ত করে, গণনা এবং সংকেতগুলিকে বৃদ্ধি করে। গ্রাহক তালা দেয়, কাউন্টারটি পরীক্ষা করে এবং যদি শূন্য-না খায় তবে শূন্য যদি শর্তে অপেক্ষা করে।
ডেভিড রদ্রিগেজ - ড্রিবিস

2
আপনি এইভাবে একটি সেমফোর অনুকরণ করতে পারেন: আপনি যে সেমফোরটি দিয়েছিলেন তার সাথে একটি ভেরিয়েবল শুরু করুন, তারপরে wait()"লক করুন, শূন্য-না-হ্রাস হয়েছে কিনা তা গণনা করে পরীক্ষা করুন; শূন্য শর্তে অপেক্ষা করুন" যখন post"লক, ইনক্রিমেন্ট কাউন্টার, যদি এটি 0 হয় তবে সিগন্যাল
ডেভিড রদ্রিগেজ - ড্রিবিয়াস

হ্যাঁ, ভাল লাগছে। আমি অবাক হই যে পোস্টিক্স সেমোফোরগুলি একইভাবে প্রয়োগ করা হয়।
25:25

@ টৌরান: আমি নিশ্চিতভাবে জানি না (এবং এটি পসিক্স ওএসের উপর নির্ভর করে) তবে আমি অসম্ভব বলে মনে করি। সেমোফোরগুলি traditionতিহ্যগতভাবে একটি "নিম্ন-স্তরের" সিঙ্ক্রোনাইজেশন আঞ্চলিক হিসাবে মুটেক্স এবং শর্ত ভেরিয়েবলের তুলনায়, এবং নীতিগতভাবে কনডওয়ারের উপরে প্রয়োগ করা হলে সেগুলির থেকে আরও কার্যকর করা যায়। সুতরাং, প্রদত্ত ওএসে আরও বেশি সম্ভাবনা রয়েছে যে সমস্ত ব্যবহারকারী-স্তরের সিঙ্ক আদিমগুলি কিছু সাধারণ সরঞ্জামগুলির উপরে তৈরি করা হয়েছে যা শিডিয়ুলারের সাথে যোগাযোগ করে।
স্টিভ জেসোপ

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();

3

সি ++ ২০ অবশেষে সেমোফোর থাকবে - std::counting_semaphore<max_count>

এগুলির (অন্তত) নিম্নলিখিত পদ্ধতিগুলি থাকবে:

  • acquire() (ব্লক)
  • try_acquire() (অবরুদ্ধ, অবিলম্বে ফেরত)
  • try_acquire_for() (অবরুদ্ধ নয়, একটি সময়কাল লাগে)
  • try_acquire_until() (অবরুদ্ধ না করা, চেষ্টা করা বন্ধ করতে এমন এক সময় নেয়)
  • release()

এই cppreference এখনো তালিকায় না থাকে, কিন্তু আপনি পড়তে পারেন এই CppCon 2019 উপস্থাপনার স্লাইড , বা ঘড়ি ভিডিও । সরকারী প্রস্তাব P0514R4 আছে , তবে আমি নিশ্চিত নই যে এটি সর্বাধিক যুগোতম সংস্করণ।


2

আমি শেয়ার্ড_পিটার এবং দুর্বল_সিপি খুঁজে পেয়েছি, একটি তালিকা সহ দীর্ঘ, আমার প্রয়োজনীয় কাজটি করেছে। আমার সমস্যাটি ছিল, আমার বেশ কয়েকটি ক্লায়েন্ট একজন হোস্টের অভ্যন্তরীণ ডেটার সাথে ইন্টারঅ্যাক্ট করতে ইচ্ছুক ছিল। সাধারণত, হোস্টটি তার নিজস্ব ডেটা আপডেট করে, তবে, যদি কোনও ক্লায়েন্ট এটির জন্য অনুরোধ করে, কোনও ক্লায়েন্ট হোস্টের ডেটা অ্যাক্সেস না করা পর্যন্ত হোস্টকে আপডেট করা বন্ধ করতে হবে। একই সময়ে, কোনও ক্লায়েন্ট একচেটিয়া অ্যাক্সেসের জন্য জিজ্ঞাসা করতে পারে, যাতে অন্য কোনও ক্লায়েন্ট বা হোস্ট সেই হোস্টের ডেটা পরিবর্তন করতে পারে না।

আমি এটি কীভাবে করেছি, আমি একটি কাঠামো তৈরি করেছি:

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;

আমি লকআপডেট, ইসআপডেটলকড এবং নিয়মিত হোস্টের আপডেট রুটিনে লকগুলির জন্য পরীক্ষা করি। লকটির জন্য পরীক্ষা করা দুর্বল_পিটারের মেয়াদ শেষ হয়ে গেছে কিনা তা পরীক্ষা করা এবং এম_লকস তালিকা থেকে কোনও মেয়াদোত্তীর্ণ অপসারণ করার মতোই সহজ (আমি কেবল হোস্ট আপডেটের সময় এটি করি), আমি তালিকাটি খালি কিনা তা পরীক্ষা করে দেখতে পারি; একই সময়ে, যখন কোনও ক্লায়েন্ট তাদের ঝুলন্ত শেয়ার্ড_পিটারটি পুনরায় সেট করে তখন স্বয়ংক্রিয়ভাবে আনলক হয়ে যায়, যখন ক্লায়েন্ট স্বয়ংক্রিয়ভাবে ধ্বংস হয়ে যায় also

সর্বোপরি প্রভাবটি হল, যেহেতু ক্লায়েন্টদের খুব কমই এক্সক্লুসিভিটির প্রয়োজন হয় (সাধারণত সংযোজন এবং মুছে ফেলার জন্য সংরক্ষিত থাকে), বেশিরভাগ সময় লকআপকে (মিথ্যা) অনুরোধ জানানো হয়, যা অ-এক্সক্লুসিভ বলে, এটি এত দিন সফল হয় (! এম_ এক্সক্লুসিভ লক)। এবং একটি লকআপডেট (সত্য), এক্সক্লুসিভিটির জন্য অনুরোধ, কেবল তখনই সফল হয় যখন উভয় (! M_exclusiveLock) এবং (m_locks.empty ()))

এক্সক্লুসিভ এবং অ-এক্সক্লুসিভ লকগুলির মধ্যে হ্রাস করার জন্য একটি সারি যুক্ত করা যেতে পারে, তবে এখনও পর্যন্ত আমার কোনও সংঘর্ষ হয়নি, সুতরাং সমাধানটি যুক্ত হওয়ার জন্য এটি না হওয়া পর্যন্ত আমি অপেক্ষা করার ইচ্ছা করি (বেশিরভাগ ক্ষেত্রেই আমার বাস্তব-বিশ্বের পরীক্ষার শর্ত রয়েছে)।

এখনও পর্যন্ত এটি আমার প্রয়োজনের জন্য ভালভাবে কাজ করছে; আমি এটি প্রসারিত করার প্রয়োজনীয়তা এবং প্রসারিত ব্যবহার সম্পর্কে উত্থাপিত হতে পারে এমন কিছু সমস্যা কল্পনা করতে পারি, তবে এটি কার্যকর করা দ্রুত ছিল এবং এর জন্য খুব কম কাস্টম কোডের প্রয়োজন হয়েছিল।


-4

যদি কেউ পারমাণবিক সংস্করণে আগ্রহী হন তবে বাস্তবায়নটি এখানে। পারফরম্যান্সটি মুটেক্স এবং শর্তের পরিবর্তনশীল সংস্করণের চেয়ে ভাল বলে আশা করা হচ্ছে।

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কোডটি বেশ কয়েকবার লুপ করতে হয়েছে। অবশেষে এটি অবরোধ মুক্ত হলে, এটি সমস্ত ভুল অনুমানযুক্ত শাখার মাকে গ্রহণ করবে কারণ সিপিইউর লুপ ভবিষ্যদ্বাণী অবশ্যই ভবিষ্যদ্বাণী করবে এটি আবার লুপ হয়ে যাবে। আমি এই কোডটি দিয়ে আরও অনেকগুলি বিষয় তালিকা করতে পারি।
ডেভিড শোয়ার্জ

1
এখানে আরেকটি সুস্পষ্ট পারফরম্যান্স হত্যাকারী: waitলুপটি স্পিন হওয়ার সাথে সাথে সিপিইউ মাইক্রোএক্সিকিউশন রিসোর্স গ্রহণ করবে। ধরা যাক এটি থ্রেডের মতো একই শারীরিক মূলের মধ্যে রয়েছে notify- এটি থ্রেডটিকে ভীষণভাবে কমিয়ে দেবে।
ডেভিড শোয়ার্জ

1
এবং এখানে আরও একটি রয়েছে: x86 সিপিইউতে (বর্তমানে সর্বাধিক জনপ্রিয় সিপিইউস), একটি তুলনা_সেটজ_উইক অপারেশন সর্বদা একটি লিখন অপারেশন হয়, এমনকি যদি এটি ব্যর্থ হয় (তুলনা ব্যর্থ হয় তবে এটি একই মানটিকে এটি আবার লিখে দেয়)। সুতরাং ধরুন দুটি কোর উভয় waitএকই সেমোফোরের জন্য একটি লুপে রয়েছে। তারা উভয়ই একই ক্যাশে লাইনে পুরো গতিতে লিখছেন , যা আন্তঃ-কোর বাসগুলি স্যাচুরেট করে অন্যান্য কোরগুলিকে একটি ক্রলের দিকে ধীর করতে পারে।
ডেভিড শোয়ার্জ

আপনার মন্তব্যগুলি দেখতে ডেভিডশওয়ার্টজ আনন্দিত '... সিপিইউর লুপ ভবিষ্যদ্বাণী ...' অংশটি নিশ্চিত তা নিশ্চিত নয়। দ্বিতীয় একটিকে সম্মত করেছেন। স্পষ্টতই আপনার তৃতীয় কেসটি ঘটতে পারে তবে মিটেক্সের সাথে তুলনা করুন যা ব্যবহারকারী-মোডকে কার্নেল-মোড স্যুইচ এবং সিস্টেম কলের কারণ হিসাবে চিহ্নিত করে, আন্তঃ-কোর সিঙ্কটি আরও খারাপ নয়।
জেফারি

1
লক ফ্রি সেম্যাফোর বলে কোনও জিনিস নেই। লক মুক্ত হওয়ার পুরো ধারণাটি মুটেক্সগুলি ব্যবহার না করে কোড লিখতে নয়, এমন কোনও কোড লিখতে হয় যেখানে কোনও থ্রেড কখনই অবরুদ্ধ হয় না। এই ক্ষেত্রে semaphore এর মূল সারাংশ থ্রেডগুলি ব্লক করা যা ওয়েট () ফাংশনটি কল করে!
কার্লো উড
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.