C中的读/写器实现

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C中的读/写器实现相关的知识,希望对你有一定的参考价值。

我目前正在学习大学的并发性。在这种情况下,我必须在C中实现读写器问题,我认为我正在走上正轨。

我对这个问题的想法是,我们需要两把锁rd_lockwr_lock。当编写器线程想要更​​改我们的全局变量时,它会尝试获取两个锁,写入全局变量并解锁。当读者想要阅读全局时,它检查wr_lock当前是否被锁定,然后读取该值,但是其中一个读者线程应该抓住rd_lock,但其他读者不应该关心rd_lock是否被锁定。

我不允许使用pthread库中已有的实现。

typedef struct counter_st {
    int value;
} counter_t;

counter_t * counter;
pthread_t * threads;

int readers_tnum;
int writers_tnum;

pthread_mutex_t rd_lock;
pthread_mutex_t wr_lock;

void * reader_thread() {

    while(true) {
        pthread_mutex_lock(&rd_lock);
        pthread_mutex_trylock(&wr_lock);
        int value = counter->value;
        printf("%d
", value);
        pthread_mutex_unlock(&rd_lock);
    }
}

void * writer_thread() {

    while(true) {
        pthread_mutex_lock(&wr_lock);
        pthread_mutex_lock(&rd_lock);

        // TODO: increment value of counter->value here.
        counter->value += 1;

        pthread_mutex_unlock(&rd_lock);
        pthread_mutex_unlock(&wr_lock);
    }
}

int main(int argc, char **args) {

    readers_tnum = atoi(args[1]);
    writers_tnum = atoi(args[2]);

    pthread_mutex_init(&rd_lock, 0);
    pthread_mutex_init(&wr_lock, 0);

    // Initialize our global variable
    counter = malloc(sizeof(counter_t));
    counter->value = 0;

    pthread_t * threads = malloc((readers_tnum + writers_tnum) * sizeof(pthread_t));
    int started_threads = 0;

    // Spawn reader threads
    for(int i = 0; i < readers_tnum; i++) {
        int code = pthread_create(&threads[started_threads], NULL, reader_thread, NULL);

        if (code != 0) {
            printf("Could not spawn a thread.");
            exit(-1);
        } else {
            started_threads++;
        }
    }

    // Spawn writer threads
    for(int i = 0; i < writers_tnum; i++) {
        int code = pthread_create(&threads[started_threads], NULL, writer_thread, NULL);

        if (code != 0) {
            printf("Could not spawn a thread.");
            exit(-1);
        } else {
            started_threads++;
        }
    }
}

目前,当使用1个阅读器和1个写入器运行时,它只会打印很多零,这意味着它从不实际执行编写器线程中的代码。我知道这不会像多个读者那样按预期工作,但是当我用其中一个读者运行它时,我不明白出了什么问题。

答案

不要把锁视为“读卡器锁”和“作者锁”。

因为您需要允许多个并发读取器,所以读者无法持有互斥锁。 (如果它们这样做,它们被序列化;只有一个可以同时持有互斥锁。)它们可以在短时间内(在它们开始访问之前,在它们结束访问之后)获取一个,以更新状态,但那是它。

将rwlock分为三个部分的时间线:“抓住rwlock”,“do work”,“释放rwlock”。

例如,您可以使用一个互斥锁,一个条件变量和一个计数器。该计数器包含活跃读者的数量。条件变量由最后一个读取器和作者在释放互斥锁之前发出信号,以唤醒等待的写入器。互斥锁保护两者,并在写操作的整个持续时间内由写入器保持。

所以,在伪代码中,你可能有

Function rwlock_rdlock:
    Take mutex
    Increment counter
    Release mutex
End Function

Function rwlock_rdunlock:
    Take mutex
    Decrement counter
    If counter == 0, Then:
        Signal_on cond
    End If
    Release mutex
End Function

Function rwlock_wrlock:
    Take mutex
    While counter > 0:
        Wait_on cond
End Function

Function rwlock_unlock:
     Signal_on cond
     Release mutex
End Function

请记住,无论何时等待条件变量,互斥锁都会在等待期间原子释放,并在线程唤醒时自动获取。因此,对于等待条件变量,线程将在等待之前和之后都具有互斥锁,但在等待本身期间不会。

现在,上述方法并不是您可能实施的唯一方法。

特别是,您可能会注意到在上面的方案中,您必须使用不同的“解锁”操作,具体取决于您是否对rwlock进行了读取或写入锁定。在POSIX pthread_rwlock_t实现中,只有一个pthread_rwlock_unlock()

无论你设计什么样的方案,重要的是检查它是否适用于所有情况:一个独立的读锁柜,一个独立的写锁柜,几个读锁柜,几个写锁柜,一个独立的写锁柜和一个读取锁定器,单独的写锁定器和几个读取锁定器,几个写入锁定器和一个单独的读取锁定器,以及几个读取和写入锁定器。

例如,让我们考虑有几个活动读者的情况,并且编写者想要写锁定rwlock。

作者抓住了互斥体。然后它注意到计数器非零,因此它开始等待条件变量。当最后一个读者 - 注意读者退出的顺序无关紧要,因为使用了一个简单的计数器! - 解锁rwlock上的读锁,它在条件变量上发出信号,唤醒编写器。然后作者抓住互斥锁,看到计数器为零,然后开始工作。在此期间,互斥锁由作者保存,因此所有新读者都将阻止,直到作者释放互斥锁。因为作者在释放互斥锁时也会在条件变量上发出信号,这是其他等待的作者和等待的读者之间的竞争,他们接下来会去。

以上是关于C中的读/写器实现的主要内容,如果未能解决你的问题,请参考以下文章

互斥锁 & 共享锁

需要示例代码片段帮助

C++11 中的读/写多线程

何时唤醒读写器问题中的作者?在释放互斥锁之前还是之后?

C 中的共享内存代码片段

在 python 代码中处理来自 C 库的对象