【问题标题】:Readers - writers C - simulate writing conflict by bad synchronizationReaders - writers C - 通过错误的同步模拟写入冲突
【发布时间】:2015-01-15 15:39:21
【问题描述】:

我有一个简单的程序来模拟 C 中的读者-作者问题。要求用户输入作者数和读者数。然后创建随机数量的写入器 - 线程和读取器 - 线程。项目的写入由全局变量 itemsCount 模拟 - 它表示新插入项目的 ID (itemsCount + 1)。我认为到目前为止,该程序运行良好。

但现在我必须展示由错误的写入同步引起的写入冲突。我认为这足以简单地删除声明 sem_wait(&w); 或严重初始化信号量 - 例如 sem_init(&w,0,5);

但它什么也没做,我看不到任何写作冲突。我想,在输出中我会看到如下内容:

Writer 1 写入项目 --> 项目数:1 |项目名称:作家1

Writer 2 写入项目 --> 项目数:1 |物品名称:作家2

(冲突:一个数字的两个项目)。但是这一切都没有发生。

我哪里错了?

具有良好同步性的代码:

#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>

#define MAX_READERS 10
#define MAX_WRITERS 10

sem_t w;    // semaphor for write access
sem_t m;    // mutex
int rc=0;   // readers count

int writersCount;   // how many writers does user want
int readersCount;   // how many readers does user want
pthread_t writersThread[MAX_WRITERS*5], readersThread[MAX_READERS*5];   // threads for writers and readers
int writeCount[MAX_WRITERS], readCount[MAX_READERS];    // how many times did each writer write and each reader read
int itemsCount=0;   // how many items is stored in the "DB"

void *writer(void *i)
{
    int a = *((int *) i);

    sem_wait(&w);   // P(w)
    printf("Writer %d writes item --> number of item: %d | name of item: Writer %d\n", a+1, ++itemsCount, a+1);
    writeCount[a]++;
    sem_post(&w);   // V(w)

    return 0;
}

void *reader(void *i)
{
    int a = *((int *) i);

    sem_wait(&m);   // P(m)
    rc++;
    if (rc == 1) {
        sem_wait(&w);   // P(w)
    }
    sem_post(&m);   // V (m)

    printf("Reader %d reads from DB.\n", a+1);
    readCount[a]++;

    sem_wait(&m);   // P(m)
    rc--;
    if (rc == 0) {
        sem_post(&w);   // V(w)
    }
    sem_post(&m);   // V(m)

    return 0;
}

int randomCount() // returns random integer between 1 and 5
{
    return 1 + 5.0 * rand() / RAND_MAX;
}

int main()
{
    srand(time(NULL));

    sem_init(&w,0,1);   // semaphore initialization
    sem_init(&m,0,1);

    int i;

    printf("Enter count of writers (max. %d):", MAX_WRITERS);
    scanf("%d",&writersCount);
    if (writersCount > MAX_WRITERS) {
        fprintf(stderr, "Max count of wirters is: %d\n", MAX_WRITERS);
        return 1;   
    }

    printf("Enter count of readers (max. %d):", MAX_READERS);
    scanf("%d",&readersCount);
    if (writersCount > MAX_READERS) {
        fprintf(stderr, "Max count of readers is %d\n", MAX_READERS);
        return 1;   
    }

    printf("---------------------------------------------\n");

    int readerIndexes[readersCount];    // reader indexes (will be passed to thread)
    int writerIndexes[readersCount];    // writer indexes (will be passed to thread)
    int totalReaders = 0;
    int totalWriters = 0;

    for (i=0; i<readersCount; i++)  // create readers (how many did user enter)
    {
        int j;
        int count;

        readerIndexes[i] = i;
        count = randomCount();
        for (j=0; j<count; j++) // let the reader read randomly from 1 to 5 times
        {
            pthread_create(&readersThread[totalReaders++], NULL, reader, &readerIndexes[i]);
        }
    }

    for (i = 0 ; i < writersCount ; i++)    // create writers (how many did user enter)
    {
        int j;
        int count;

        writerIndexes[i] = i;
        count = randomCount();
        for (j=0;j<count;j++)   // let the writer write randomly from 1 to 5 times
        {
            pthread_create(&writersThread[totalWriters++], NULL, writer, &writerIndexes[i]);
        }
    }

    for (i=0;i<totalWriters;i++) // join the threads
    {
        pthread_join(writersThread[i], NULL);
    }

    for (i=0;i<totalReaders;i++)
    {
        pthread_join(readersThread[i], NULL);
    }

    printf("---------------------------------------------\n");

    for (i=0;i<readersCount;i++)
    {
        printf("Reader %d read %d times\n", i+1, readCount[i]);
    }
    for (i=0;i<writersCount;i++)
    {
        printf("Writer %d wrote %d times\n", i+1, writeCount[i]);
    }

    sem_destroy(&w);
    sem_destroy(&m);
    return 0;
}

输出:

Enter count of writers (max. 10):5
Enter count of readers (max. 10):5
---------------------------------------------
Reader 1 reads from DB.
Reader 1 reads from DB.
Reader 2 reads from DB.
Reader 2 reads from DB.
Reader 2 reads from DB.
Reader 2 reads from DB.
Reader 3 reads from DB.
Reader 3 reads from DB.
Reader 4 reads from DB.
Reader 4 reads from DB.
Reader 4 reads from DB.
Writer 1 writes item --> number of item: 1 | name of item: Writer 1
Writer 1 writes item --> number of item: 2 | name of item: Writer 1
Writer 1 writes item --> number of item: 3 | name of item: Writer 1
Writer 2 writes item --> number of item: 4 | name of item: Writer 2
Writer 2 writes item --> number of item: 5 | name of item: Writer 2
Writer 2 writes item --> number of item: 6 | name of item: Writer 2
Writer 3 writes item --> number of item: 7 | name of item: Writer 3
Writer 3 writes item --> number of item: 8 | name of item: Writer 3
Writer 3 writes item --> number of item: 9 | name of item: Writer 3
Writer 3 writes item --> number of item: 10 | name of item: Writer 3
Writer 4 writes item --> number of item: 11 | name of item: Writer 4
Writer 5 writes item --> number of item: 12 | name of item: Writer 5
Writer 5 writes item --> number of item: 13 | name of item: Writer 5
Writer 5 writes item --> number of item: 14 | name of item: Writer 5
Reader 4 reads from DB.
Reader 4 reads from DB.
Reader 5 reads from DB.
Reader 5 reads from DB.
Reader 5 reads from DB.
Reader 5 reads from DB.
---------------------------------------------
Reader 1 read 2 times
Reader 2 read 4 times
Reader 3 read 2 times
Reader 4 read 5 times
Reader 5 read 4 times
Writer 1 wrote 3 times
Writer 2 wrote 3 times
Writer 3 wrote 4 times
Writer 4 wrote 1 times
Writer 5 wrote 3 times

非常感谢

【问题讨论】:

  • 也许我有解决方案。需要给writer写类似的代码:usleep(rand() % 10); - 假装一些动作。但我不确定为什么?是不是因为不然线程会跑得太快?
  • 另一件事是将全局变量加载到线程的局部变量中,增加局部变量,然后将其再次传递给全局变量。这个作家的代码完美地工作:
  • void *writer(void *i) { int a = *((int *) i); int myItemsCount; sem_wait(&amp;w); // P(w) myItemsCount = itemsCount; myItemsCount++; usleep(rand() % 10); itemsCount = myItemsCount; printf("Writer %d writes item --&gt; number of item: %d | name of item: Writer %d\n", a+1, myItemsCount, a+1); writeCount[a]++; sem_post(&amp;w); // V(w) return 0; }

标签: c


【解决方案1】:

您几乎没有机会看到操作系统调度程序在唤醒后的几条指令 (sem_wait) 和内存屏障之前的一条指令 (sem_post) 中断您的线程。

从日志中可以看出,读取器只是按顺序执行,没有不必要的上下文切换(干得好,调度程序!)。

我认为如果你这样重写你的代码:

int v = writeCount[a], cnt;
for (int cnt = 0; cnt < 1000; cnt++) {;} // some huge spinlock
writeCount[a] = v + 1;

- 您将能够证明写入冲突。

【讨论】:

  • 谢谢,我明白了。我是对的,使用 usleep(rand() % 10) 会产生同样的效果吗?
  • sem_post() 释放一个信号量,并且与内存屏障不(直接)相关。 See this
  • ring0:它的实现已定义,但Linux kernel documentation 表示释放信号量意味着 CPU 对重新排序产生了某些障碍。当然,这并不是最严格意义上的内存屏障,因为效果可能仅限于一个 CPU(它缓存了包含的内存单元)
  • Pavel:usleep() 绝对不等于循环。 POSIX 声明“usleep() 函数将导致调用线程暂停执行”。有些代码曾经使用 usleep(0) 作为“线程让出”操作)这种可能性使得使用 usleep() 的演示不如使用繁忙循环的演示公平。
  • 好的,我明白了。请问你知道为什么用usleep有一个写作冲突,但用loop没有一个吗?谢谢
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-08
  • 2019-10-19
  • 1970-01-01
  • 1970-01-01
  • 2019-09-09
相关资源
最近更新 更多