【问题标题】:How to let a writer thread starve如何让作家线程饿死
【发布时间】:2016-10-30 02:46:42
【问题描述】:

我使用 C++14 的 shared_timed_mutex 编写了读写器问题的实现。在我看来,下面的代码应该会导致写入器饿死,因为太多的读取器线程一直在数据库上工作(在这个例子中是一个简单的数组):写入器没有机会获得锁。

mutex cout_mtx;                 // controls access to standard output
shared_timed_mutex db_mtx;      // controls access to data_base

int data_base[] = { 0, 0, 0, 0, 0, 0 };

const static int NR_THREADS_READ = 10; 
const static int NR_THREADS_WRITE = 1;
const static int SLEEP_MIN = 10;
const static int SLEEP_MAX = 20;

void read_database(int thread_nr) {
    shared_lock<shared_timed_mutex> lck(db_mtx, defer_lock); // create a lock based on db_mtx but don't try to acquire the mutex yet
    while (true) {
        // generate new random numbers
        std::random_device r;
        std::default_random_engine e(r());
        std::uniform_int_distribution<int> uniform_dist(SLEEP_MIN, SLEEP_MAX);
        std::uniform_int_distribution<int> uniform_dist2(0, 5);
        int sleep_duration = uniform_dist(e);   // time to sleep between read requests 
        int read_duration = uniform_dist(e);    // duration of reading from data_base
        int cell_number = uniform_dist2(e);     // what data cell will be read from
        int cell_value = 0;

        // wait some time before requesting another access to the database
        this_thread::sleep_for(std::chrono::milliseconds(sleep_duration));

        if (!lck.try_lock()) {

            lck.lock(); // try to get the lock in blocked state
        }

        // read data
        cell_value = data_base[cell_number];

        lck.unlock();

    }
}

void write_database(int thread_nr) {
    unique_lock<shared_timed_mutex> lck(db_mtx, defer_lock); // create a lock based on db_mtx but don't try to acquire the mutex yet
    while (true) {
        // generate new random numbers
        std::random_device r;
        std::default_random_engine e(r());
        std::uniform_int_distribution<int> uniform_dist(SLEEP_MIN, SLEEP_MAX);
        std::uniform_int_distribution<int> uniform_dist2(0, 5);
        int sleep_duration = uniform_dist(e);   // time to sleep between write requests 
        int read_duration = uniform_dist(e);    // duration of writing to data_base
        int cell_number = uniform_dist2(e);     // what data cell will be written to

        // wait some time before requesting another access to the database
        this_thread::sleep_for(std::chrono::milliseconds(sleep_duration));

        // try to get exclusive access
        cout_mtx.lock();
        cout << "Writer <" << thread_nr << "> requesting write access." << endl;
        cout_mtx.unlock();

        if (!lck.try_lock()) {

            lck.lock(); // try to get the lock in blocked state
        }

        // write data
        data_base[cell_number] += 1;

        lck.unlock();

    }
}

当线程读取、写入、尝试以阻塞模式或通过try_lock() 方法获取锁时,我向标准输出添加了一些输出,但为了清楚起见,我删除了输出。我在 main 方法中进一步启动线程。当我运行程序时,编写器总是有机会写入数组(导致所有读取器线程阻塞,这没关系),但正如我上面所说,编写器根本不应该能够访问,因为有太多许多读取器线程从数组中读取。即使我根本不让阅读器线程进入睡眠状态(参数 0),编写器线程也会以某种方式找到获取互斥锁的方法。那我怎么让作者饿死呢?

【问题讨论】:

  • 你使用的是哪个 std::lib?
  • @HowardHinnant 只是 C++11/14 内部同步机制:
  • 哦,我想知道gcc的libstdc++、VS、libc++?不重要,只是好奇。
  • @HowardHinnant 对不起,我误解了你的问题。我使用 VS,但有时 g++ 只是为了看看发生了什么......

标签: c++ multithreading c++14 starvation


【解决方案1】:

std::shared_timed_mutex 的高质量实现不会让读者和作者挨饿。然而,随着读者数量/作者数量的增加,作者获得锁的概率越小。使用您当前的设置(1 位作者对 10 位读者),我猜作者大约有 9% 的时间会获得锁定。当您增加该比率时,写入器获得的锁将减少,但永远不会 100% 被饿死。

如果你只让作者在try_lock下获取,那么你100%饿死它的机会会大大增加。

存在允许std::shared_timed_mutex 实现而不使读者或作者挨饿的算法的存在是std::shared_timed_mutex 没有允许您指定读者优先级或作者优先级的API 的原因。

算法

假设互斥体中有两个门:gate1gate2

要通过gate1,(几乎)无论您是读者还是作家都无关紧要。如果gate1 内有其他作者,则无法进入。读者必须遵循在实践中永远不会发挥作用的附加规则:如果已经有超过 gate1 的最大读者数,则可以'进不去。

一旦通过gate1,读者就拥有共享锁。

一旦通过gate1,作者确实拥有唯一锁。他必须在gate2 外面进一步等待,直到没有更多的读者持有共享锁。一旦通过gate2,作者就拥有了唯一的锁。

这个算法是“公平的”,因为如果你是读者或作家,通过gate1 几乎没有什么区别。如果gate1 之外有一堆读写器,则下一个进入的线程由操作系统决定,而不是由该算法决定。所以你可以把它想象成掷骰子。如果您与竞争gate1 的作者拥有相同数量的读者,那么下一个通过gate1 的读者或作者的可能性为50/50。

【讨论】:

  • 感谢您非常清楚的解释!这真的很有帮助!你能给我你读到这个的资源(如果有的话)吗?
  • @kimsay:当然。我手边没有很好的参考资料,但这是我最好的参考资料:open-std.org/jtc1/sc22/wg21/docs/papers/2007/…
猜你喜欢
  • 2021-06-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-19
相关资源
最近更新 更多