简体   繁体   English

使用pthread的C语言中的读取器/写入器问题

[英]Reader/Writer problem in C using pthreads

I am learning pthreads when I came across the reader/writer problem in C. The problem is very simple where the "writer" threads will be accessing data from an external source and the "reader" threads will be reading this data from the shared buffer and then pseudo processing the data. 当我遇到C语言中的读取器/写入器问题时,我正在学习pthread。问题非常简单,其中“写入器”线程将从外部源访问数据,而“读取器”线程将从共享缓冲区读取数据然后对数据进行伪处理。 The reader and writer threads need to run continuously in a while loop. 读取器和写入器线程需要在while循环中连续运行。

I am trying to implement and compile this on a standard unix system with a POSIX interface. 我正在尝试使用POSIX接口在标准的UNIX系统上实现和编译此文件。

I have looked through some of the stack overflow questions: 我已经看过一些堆栈溢出问题:

Reader Writer program in C using mutexes and pthreads 使用互斥和pthread的C语言中的Reader Writer程序

reader/writer lock in pthread pthread中的读取器/写入器锁定

and I have gotten no where with these. 而我却一无所获。

Here is what I have so far: 这是我到目前为止的内容:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
#include <assert.h>

#define BUFF_SIZE 50
#define M 10
#define N 20

int get_external_data(char *buffer, int bufferSizeInBytes);
void process_data(char *buffer, int bufferSizeInBytes);

int get_external_data(char *buffer, int bufferSizeInBytes){
    int status;
    int val;
    char srcString[] = "0123456789abcdefghijklmnopqrstuvwxyxABCDEFGHIJKLMNOPQRSTUVWXYZ";

    val = (int)(rand() % 62);
    if (bufferSizeInBytes < val){
        return (-1);
    }

    strncpy(buffer, srcString, val);
    return val;
}

void process_data(char *buffer, int bufferSizeInBytes){
    int i;
    if(buffer) {
        printf("thread %li - ", pthread_self());

        for(i = 0; i < bufferSizeInBytes; i++) {
            printf("%c", buffer[i]);
        }

        printf("\n");
        memset(buffer, 0, bufferSizeInBytes);
    } else {
        printf("error in process data - %li\n", pthread_self());
    }

    return;
}


pthread_mutex_t data_lock = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t print_lock = PTHREAD_MUTEX_INITIALIZER;
sem_t data_count;

typedef struct node {
    struct node *next;
    char *data;
    int length;
} node_t;

node_t *head, *tail;

/**
 * This thread is responsible for pulling data off of the shared data 
 * area and processing it using the process_data() API.
 */

void *reader_thread(void *arg) {
    int rc;
    node_t *removed_node;

    while(1) {
        rc = sem_wait(&data_count);
        if (0 != rc) {
            return NULL;
        }

        pthread_mutex_lock(&data_lock);

        assert(NULL != head);
        removed_node = head;
        head = head->next;

        pthread_mutex_unlock(&data_lock);

        //Adding this lock for sake of readability at the cost of reduced consumption rate...will run out of memory eventually.

        pthread_mutex_lock(&print_lock);

        process_data(removed_node->data, removed_node->length);

        pthread_mutex_unlock(&print_lock);

        free(removed_node->data);
        free(removed_node);
    }

    return NULL;
}


/**
 * This thread is responsible for pulling data from a device using
 * the get_external_data() API and placing it into a shared area
 * for later processing by one of the reader threads.
 */

void *writer_thread(void *arg) {
    int length;
    char *buffer;
    node_t *new_node;

    new_node = (node_t*) malloc(sizeof(*new_node));
    buffer = (char*) malloc(sizeof(*buffer) * BUFF_SIZE);

    while(1) {

        length = get_external_data(buffer, BUFF_SIZE);

        if (length == -1) {
            //data too big, discard it and try again;
            continue;
        }

        new_node->next = NULL;
        new_node->length = length;
        new_node->data = buffer;

        pthread_mutex_lock(&data_lock);


        if (head == NULL) { //The linked list is completely empty   
            head = new_node;
            tail = new_node;        
        } else { //There are items in the list and we're appending  
            tail->next = new_node;
            tail = new_node;
        }

        pthread_mutex_unlock(&data_lock);

        pthread_mutex_lock(&print_lock);

        printf("thread %ld wrote - %s \n", pthread_self(), buffer);

        pthread_mutex_unlock(&print_lock);

        sem_post(&data_count);

        buffer = (char*) malloc(sizeof(*buffer) * BUFF_SIZE);
        new_node = (node_t*) malloc(sizeof(*new_node));
    }

    return NULL;
}

int main(int argc, char **argv) {
    int i = sem_init(&data_count, 0, 0);
    pthread_t dummy; //creating a dummy thread

    for(i = 0; i < N; i++) { 
        pthread_create(&dummy, NULL, reader_thread, NULL);
    }

    for(i = 0; i < M; i++) { 
        pthread_create(&dummy, NULL, writer_thread, NULL);
    }

    sleep(100);
    return 0;   
}

The thread synchronization is there without any compilation errors but my program stops after the writer thread writes on a buffer a few times. 那里有线程同步,没有任何编译错误,但是在编写器线程在缓冲区上写了几次之后,我的程序停止了。

The reader and writer threads need to run continuously in a while loop, but they don't in my case. 读取器和写入器线程需要在while循环中连续运行,但在我看来,它们不是必需的。

Any idea on how to resolve this issue? 关于如何解决此问题的任何想法?

With gcc add the option -lpthread to link with the pthread library, I also encourage you to add the options -pedantic -Wextra -Wall 使用gcc添加-lpthread选项以与pthread库链接,我也鼓励您添加-pedantic -Wextra -Wall选项

There are several problems in your program, some are indicated by the compiler 您的程序中有几个问题,有些是编译器指出的

In : 在:

 printf("thread %i - ", pthread_self()); printf("error in process data - %i\\n", pthread_self()); 

In the POSIX standard it is not required that pthread_t is an arithmetic type, so it can be a struct etc and you cannot write it as an int . 在POSIX标准中,不需要pthread_t是算术类型,因此它可以是struct等,并且不能将其写为int May be in your case it is an int (more probably an unsigned long ) but this is not portable, and it is better to manage by yourself an identifier associated to each thread. 在您的情况下,它可能是一个int (很可能是unsigned long ),但这不是可移植的,因此最好自己管理与每个线程关联的标识符。

In reader_thread : reader_thread

 return; 

but the function return a void* , replace it by return NULL; 但是函数返回一个void* ,用return NULL;代替return NULL; for instance 例如

In writer_thread : writer_thread

 printf("thread %d wrote - %s", buffer); 

has three problems: 有三个问题:

  • an argument of type int is missing (probably you wanted pthread_self() ) 类型为int的参数丢失(可能是您想要pthread_self()
  • buffer is set by get_external_data using strncpy , so the null ending character is not present but required by printf %s (when the missing argument will be added or %d removed) 缓冲区是由get_external_data使用strncpy设置的,因此存在空结束符,但printf%s必需(当缺少的参数将被添加或%d被删除时)
  • buffer can be freed by reader_thread , probably you supposed it is protected by the semaphore but this is not the case 缓冲区可以通过释放reader_thread ,也许你认为它是由信号量保护,但这情况并非如此

In get_external_data status is unused get_external_data 状态未使用

In main 主要

  usleep(100); 

is a very short time given to the threads to work, and in fact you just want to be blocked, so you can replace it by pthread_join(dummy, NULL); 这是给线程工作的非常短的时间,实际上您只想被阻塞,因此可以将其替换为pthread_join(dummy, NULL);


A proposal to have thread identifiers : allocate an int in the heap, set it with a unique number and give it in parameter to the created threads : 有线程标识符的建议:在堆中分配一个int ,为它设置一个唯一的数字,然后将in参数提供给创建的线程:

int main() {
    ...
    for(i = 0; i < N; i++) { 
      int * m = malloc(sizeof(int));

      *m = i;
      pthread_create(&dummy, NULL, reader_thread, m);
    }

    for(i = 0; i < M; i++) { 
      int * m = malloc(sizeof(int));

      *m = 100 + i;
      pthread_create(&dummy, NULL, writer_thread, m);
    }
    ...
}

and

void *writer_thread(void *arg) {
    int id = *((int*) arg);
    ...
    free((int*) arg);
    ...
    printf("thread %d wrote - %s", id, buffer);
    ...
}

and

void *reader_thread(void *arg) {
    int id = *((int*) arg);
    ...
    free((int*) arg);
    ...
    process_data(removed_node->data, removed_node->length, id);
    ...
}

and

void process_data(char *buffer, int bufferSizeInBytes, int id){
    ...
        printf("thread %i - ", id);
...
        printf("error in process data - %i\n", id);
...
}

After the previous changes an execution under valgrind with errors dues to the other problems : 在上次更改之后,由于其他问题,在valgrind下执行的操作会出现错误:

pi@raspberrypi:/tmp $ gcc -g -pedantic -Wextra -Wall t.c -lpthread
pi@raspberrypi:/tmp $ valgrind ./a.out
==3847== Memcheck, a memory error detector
==3847== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==3847== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==3847== Command: ./a.out
==3847== 
==3847== Thread 22:
==3847== Conditional jump or move depends on uninitialised value(s)
==3847==    at 0x484B20C: strlen (vg_replace_strmem.c:458)
==3847==    by 0x48FD68F: vfprintf (vfprintf.c:1637)
==3847==    by 0x4902ADF: printf (printf.c:33)
==3847==    by 0x10B83: writer_thread (t.c:144)
==3847==    by 0x4898FC3: start_thread (pthread_create.c:458)
==3847==    by 0x498D037: ??? (clone.S:76)
==3847== 
thread 100 wrote - 0123456789abcdefghijklmnopqrstuvwxyxABCDEthread 103 wrote - 0123456789abcdefghijklmnopqrstuvwxyxthread 1 - 0123456789abcdefghijklmnopqrstuvwxyxABCDE
thread 0 - 0123456789abcdefghijklmnopqrstuvwxyx
thread 102 wrote - 0123456789abcdefghijklmnopqrstuvwxyxABthread 103 wrote - 0123456thread 2 - 012
thread 101 wrote - 0123456789abcdefghithread 105 wrote - 0123456789abcdefghijklmnopqrstuvwxyxABCDEFGHIJKLMthread 3 - 0123456
thread 109 wrote - 0123456789abcdefghijklmnopqrstuvwxyxAthread 3 - 0123456789abcdefghi
==3847== Invalid read of size 1
==3847==    at 0x484B1EC: strlen (vg_replace_strmem.c:458)
==3847==    by 0x48FD68F: vfprintf (vfprintf.c:1637)
==3847==    by 0x4902ADF: printf (printf.c:33)
==3847==    by 0x10B83: writer_thread (t.c:144)
==3847==    by 0x4898FC3: start_thread (pthread_create.c:458)
==3847==    by 0x498D037: ??? (clone.S:76)
==3847==  Address 0x49fcd18 is 0 bytes inside a block of size 50 free'd
==3847==    at 0x4848B8C: free (vg_replace_malloc.c:530)
==3847==  Block was alloc'd at
==3847==    at 0x4847568: malloc (vg_replace_malloc.c:299)
==3847== 
thread 107 wrote - 0123456789abcdefgthread 100 wrote - thread 100 wrote - 0123456789athread 101 wrote - 0123456789abcthread 7 - 0123456789abcdefghijklmnopqrstuvwxyxABCDEFGHIJKLM
thread 7 - 0123456789abcdefghijklmnopqrstuvwxyxAB
thread 9 - 0123456789abc
thread 9 - 0123456789abcdefghijklmnopqrstuvwxyxA
thread 108 wrote - 0123456789abcdefghijklmnopqrstuvwxyxABthread 105 wrote - 01thread 8 - 0123456789ab
thread 108 wrote - 0123456789abcdefghithread 12 - 0123
thread 103 wrote - 0123456789abcdefghijklmnopqrstuvwxyxABCDEFGthread 101 wrote - 0123456789abcdefghijklmnopqrstuvthread 103 wrote - 0123456789abcdefghijklmnopqrstuvwxyxABCDEFGthread 101 wrote - 0123456789abcdefghijklmnopqrstuvthread 14 - 0123456789a
thread 101 wrote - 0123456789abcdefghijklthread 101 wrote - 0123456789abcdefghijklmnopqrstuvwxyxthread 101 wrote - 0123456789abcdefghijklmnopqrstuvwxyxABCDEFGHthread 101 wrote - 0123456789abcdefghijklmnopqthread 101 wrote - 0123456789abcdefghijklmnopqrstuthread 101 wrote - 0123thread 102 wrote - thread 2 - 0123456789abcdefghijklmnopqrstuv
thread 2 - 0123456789abcdefghijklmnopqr
thread 2 - 0123456789abcdefghi
thread 2 - 0123456789abcdefghijklmnopqrstuvwxyxABCDEFG
thread 100 wrote - 0123456789abcthread 100 wrote - 0123456789abcdefghijklmnopqrstuvwxyxABCDEFGHIJKthread 4 - 0123456789abcdefghijkl
thread 109 wrote - thread 13 - 0123456789abcdefghijklmnopqrstuvwxyxAB
thread 108 wrote - 0123456789abcdefghijklmnopqrstuvwxyxABCDEFGHIthread 19 - 0123456789abc
thread 101 wrote - 0thread 18 - 01
thread 108 wrote - 0123456thread 108 wrote - 01thread 108 wrote - 0123456789abcdefghijthread 10 - 0123456789abcdefghijklmnopq
thread 8 - 0123456789abcdefghijklmnopqrstu
==3847== Thread 30:
==3847== Conditional jump or move depends on uninitialised value(s)
==3847==    at 0x484B1F4: strlen (vg_replace_strmem.c:458)
==3847==    by 0x48FD68F: vfprintf (vfprintf.c:1637)
==3847==    by 0x4902ADF: printf (printf.c:33)
==3847==    by 0x10B83: writer_thread (t.c:144)
==3847==    by 0x4898FC3: start_thread (pthread_create.c:458)
==3847==    by 0x498D037: ??? (clone.S:76)
==3847== 
==3847== Conditional jump or move depends on uninitialised value(s)
==3847==    at 0x48FBEEC: vfprintf (vfprintf.c:1637)
==3847==    by 0x4902ADF: printf (printf.c:33)
==3847==    by 0x10B83: writer_thread (t.c:144)
==3847==    by 0x4898FC3: start_thread (pthread_create.c:458)
==3847==    by 0x498D037: ??? (clone.S:76)
==3847== 
==3847== Conditional jump or move depends on uninitialised value(s)
==3847==    at 0x48FBF0C: vfprintf (vfprintf.c:1637)
==3847==    by 0x4902ADF: printf (printf.c:33)
==3847==    by 0x10B83: writer_thread (t.c:144)
==3847==    by 0x4898FC3: start_thread (pthread_create.c:458)
==3847==    by 0x498D037: ??? (clone.S:76)
==3847== 
==3847== Conditional jump or move depends on uninitialised value(s)
==3847==    at 0x49245B8: _IO_file_xsputn@@GLIBC_2.4 (fileops.c:1294)
==3847==    by 0x48FBF7B: vfprintf (vfprintf.c:1637)
==3847==    by 0x4902ADF: printf (printf.c:33)
==3847==    by 0x10B83: writer_thread (t.c:144)
==3847==    by 0x4898FC3: start_thread (pthread_create.c:458)
==3847==    by 0x498D037: ??? (clone.S:76)
==3847== 
==3847== Conditional jump or move depends on uninitialised value(s)
==3847==    at 0x48FBF80: vfprintf (vfprintf.c:1637)
==3847==    by 0x4902ADF: printf (printf.c:33)
==3847==    by 0x10B83: writer_thread (t.c:144)
==3847==    by 0x4898FC3: start_thread (pthread_create.c:458)
==3847==    by 0x498D037: ??? (clone.S:76)
==3847== 
==3847== Conditional jump or move depends on uninitialised value(s)
==3847==    at 0x48FBF90: vfprintf (vfprintf.c:1637)
==3847==    by 0x4902ADF: printf (printf.c:33)
==3847==    by 0x10B83: writer_thread (t.c:144)
==3847==    by 0x4898FC3: start_thread (pthread_create.c:458)
==3847==    by 0x498D037: ??? (clone.S:76)
==3847== 
==3847== Conditional jump or move depends on uninitialised value(s)
==3847==    at 0x48FBE24: vfprintf (vfprintf.c:1668)
==3847==    by 0x4902ADF: printf (printf.c:33)
==3847==    by 0x10B83: writer_thread (t.c:144)
==3847==    by 0x4898FC3: start_thread (pthread_create.c:458)
==3847==    by 0x498D037: ??? (clone.S:76)
==3847== 
==3847== Conditional jump or move depends on uninitialised value(s)
==3847==    at 0x48FBE6C: vfprintf (vfprintf.c:1668)
==3847==    by 0x4902ADF: printf (printf.c:33)
==3847==    by 0x10B83: writer_thread (t.c:144)
==3847==    by 0x4898FC3: start_thread (pthread_create.c:458)
==3847==    by 0x498D037: ??? (clone.S:76)
==3847== 
thread 108 wrote - thread 108 wrote - 0123456789abcdefghijkthread 12 - 0123456789abcdefghijklmnopqrstuvwxyxABCDEFGHIJKL
thread 108 wrote - 0123456789abcdefghijthread 106 wrote - thread 5 - 0123456789abcdefg
thread 106 wrote - thread 106 wrote - 0123456789abcdefghijklmnopqrstuvwxyxABCDthread 16 - 0123456789abcdefghijklmnopqrstuvwx
thread 16 - 0123456789abcdefghijklmnopqrstuv
==3847== 
==3847== HEAP SUMMARY:
==3847==     in use at exit: 5,444 bytes in 74 blocks
==3847==   total heap usage: 155 allocs, 81 frees, 8,138 bytes allocated
==3847== 
==3847== LEAK SUMMARY:
==3847==    definitely lost: 0 bytes in 0 blocks
==3847==    indirectly lost: 0 bytes in 0 blocks
==3847==      possibly lost: 4,080 bytes in 30 blocks
==3847==    still reachable: 1,364 bytes in 44 blocks
==3847==         suppressed: 0 bytes in 0 blocks
==3847== Rerun with --leak-check=full to see details of leaked memory
==3847== 
==3847== For counts of detected and suppressed errors, rerun with: -v
==3847== Use --track-origins=yes to see where uninitialised values come from
==3847== ERROR SUMMARY: 51 errors from 10 contexts (suppressed: 6 from 3)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM