Is it possible to access/update the child thread's resource from parent thread?

后端 未结 1 1586
心在旅途
心在旅途 2020-12-11 14:19

I\'m doing a socket programming in C and I\'m completely new to Multithreading.

This is my scenario, I need a parent thread which reads the data from socket(lets say

相关标签:
1条回答
  • 2020-12-11 14:41

    Thread-safe queue that supports multiple producers and consumers.

    MtQueue.h:

    #ifndef MtQueue_H
    #define MtQueue_H
    
    #include <pthread.h>
    #include <stdlib.h>
    
    // A fixed-size circular buffer.
    typedef struct {
       pthread_mutex_t mutex;
       pthread_cond_t cond;
       int done;
       int empty;
       int full;
       size_t max;
       size_t next_insert;
       size_t next_read;
       void** buf;
    } MtQueue;
    
    // Returns NULL and sets errno on error.
    // Free the queue with MtQueue_delete when done.
    MtQueue* MtQueue_new(size_t max);
    
    // Returns 0 and sets errno on error.
    // Destroy the queue with MtQueue_destroy when done.
    int MtQueue_init(MtQueue* q, size_t max);
    
    // Inverse of MtQueue_new.
    // Only call when the queue is no longer in use.
    void MtQueue_delete(MtQueue* q);
    
    // Inverse of MtQueue_init.
    // Only call when the queue is no longer in use.
    void MtQueue_destroy(MtQueue* q);
    
    // Initiates shutdown of the queue.
    // You must ensure that no there are no pending call to enqueue before this is called.
    // You must ensure not to call enqueue once this is called.
    void MtQueue_done(MtQueue* q);
    
    // Returns the oldest item from the queue (via a parameter) and returns 1.
    // If the queue is empty and done, returns 0.
    // If the queue is empty and not done, waits until that changes.
    int MtQueue_dequeue(MtQueue* q, void** pp);
    
    // Adds the argument to the queue.
    // If the queue is full, waits until that changes.
    void MtQueue_enqueue(MtQueue* q, void* p);
    
    #endif
    

    MtQueue.c:

    #include <assert.h>
    #include <errno.h>
    #include <pthread.h>
    #include <stdlib.h>
    
    #include "MtQueue.h"
    
    MtQueue* MtQueue_new(size_t max) {
       MtQueue* q = malloc(sizeof(MtQueue));
       if (!q)
          goto Error1;
    
       if (!MtQueue_init(q, max))
          goto Error2;
    
       return q;
    
    Error2:
       free(q);
    Error1:
       return NULL;
    }
    
    int MtQueue_init(MtQueue* q, size_t max) {
       void** buf = malloc(sizeof(void*) * max);
       if (!buf)
          goto Error1;
    
       errno = pthread_mutex_init(&(q->mutex), NULL);
       if (errno)
          goto Error2;
    
       errno = pthread_cond_init(&(q->cond), NULL);
       if (errno)
          goto Error3;
    
       q->done = 0;
       q->empty = 1;
       q->full = 0;
       q->max = max;
       q->next_insert = 0;
       q->next_read = 0;
       q->buf = buf;
       return 1;
    
    Error3:
       pthread_mutex_destroy(&(q->mutex));
    Error2:
       free(buf);
    Error1:
       return 0;
    }
    
    void MtQueue_delete(MtQueue* q) {
       MtQueue_destroy(q);
       free(q);
    }
    
    void MtQueue_destroy(MtQueue* q) {
       assert(q->empty);
       free(q->buf);
       pthread_cond_destroy(&(q->cond));
       pthread_mutex_destroy(&(q->mutex));
    }
    
    void MtQueue_done(MtQueue* q) {
       pthread_mutex_lock(&(q->mutex));
       q->done = 1;
       pthread_cond_signal(&(q->cond));
       pthread_mutex_unlock(&(q->mutex));
    }
    
    int MtQueue_dequeue(MtQueue* q, void** pp) {
       pthread_mutex_lock(&(q->mutex));
       while (q->empty && !q->done)
          pthread_cond_wait(&(q->cond), &(q->mutex));
    
       int dequeued;
       if (q->empty) {
          // q->done && q->empty is true.
          // We are completely done.
          dequeued = 0;
       } else {
          *pp = q->buf[ q->next_read ];
          q->next_read = ( q->next_read + 1 ) % q->max;
          q->empty = q->next_read == q->next_insert;
          q->full = 0;
          dequeued = 1;
       }
    
       pthread_cond_signal(&(q->cond));
       pthread_mutex_unlock(&(q->mutex));
       return dequeued;
    }
    
    void MtQueue_enqueue(MtQueue* q, void* p) {
       pthread_mutex_lock(&(q->mutex));
       while (q->full)
          pthread_cond_wait(&(q->cond), &(q->mutex));
    
       assert(!q->done);
    
       q->buf[q->next_insert] = p;
       q->next_insert = ( q->next_insert + 1 ) % q->max;
       q->empty = 0;
       q->full = q->next_insert == q->next_read;
    
       pthread_cond_signal(&(q->cond));
       pthread_mutex_unlock(&(q->mutex));
    }
    

    a.c (Example user):

    #include <errno.h>
    #include <inttypes.h>
    #include <pthread.h>
    #include <stdint.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <time.h>
    
    #include "MtQueue.h"
    
    // Producers will block if there are this many items in the queue.
    #ifndef QUEUE_SIZE
    #define QUEUE_SIZE 10
    #endif
    
    // The number of consumers (worker threads) to create.
    #ifndef NUM_WORKERS
    #define NUM_WORKERS 4
    #endif
    
    // The amount of work to generate for this test.
    #ifndef NUM_JOBS
    #define NUM_JOBS 40
    #endif
    
    // Simulate work using a sleep.
    #ifndef SIM_WORK
    #define SIM_WORK 0
    #endif
    
    #if SIM_WORK
    static int msleep(long msec) {
       struct timespec ts;
       int res;
    
       if (msec < 0) {
           errno = EINVAL;
           return -1;
       }
    
       ts.tv_sec = msec / 1000;
       ts.tv_nsec = (msec % 1000) * 1000000;
    
        do {
           res = nanosleep(&ts, &ts);
        } while (res && errno == EINTR);
    
        return res;
    }
    #endif
    
    // Shared variables.
    static MtQueue q;
    
    static void* worker_func(void* worker_id_) {
       uintptr_t worker_id = (uintptr_t)worker_id_;
    
    #if SIM_WORK
       unsigned int seed = worker_id;  // Whatever.
    #endif
    
       uintptr_t j;
       while (MtQueue_dequeue(&q, (void**)&j)) {
          printf("[%" PRIuPTR "] Dequeued %" PRIuPTR "\n", worker_id, j);
    #if SIM_WORK
          msleep( rand_r(&seed) % 1000 + 1000 );  // Simulate a 1 to 2s load.
    #endif
          printf("[%" PRIuPTR "]    Finished processing %" PRIuPTR "\n", worker_id, j);
       }
    
       return NULL;
    }
    
    int main(void) {
       MtQueue_init(&q, QUEUE_SIZE);
    
       pthread_t workers[NUM_WORKERS];
       for (uintptr_t w=0; w<NUM_WORKERS; ++w) {
          if (errno = pthread_create(&(workers[w]), NULL, worker_func, (void*)w)) {
             perror(NULL);
             exit(1);
          }
       }
    
       for (uintptr_t j=0; j<NUM_JOBS; ++j) {
          printf("[x] Enqueuing %" PRIuPTR "...\n", j);
          MtQueue_enqueue(&q, (void*)j);
          printf("[x]    Enqueued %" PRIuPTR ".\n", j);
       }
    
       MtQueue_done(&q);
       printf("[x] Called done.\n");
    
       for (uintptr_t w=0; w<NUM_WORKERS; ++w)
          pthread_join(workers[w], NULL);
    
       MtQueue_destroy(&q);
       return 0;
    }
    

    How to run example user:

    gcc -Wall -Wextra -pedantic a.c MtQueue.c -o a -lpthread && ./a
    
    gcc -D SIM_WORK=1 -D NUM_JOBS=20 -Wall -Wextra -pedantic a.c MtQueue.c -o a -pthread && ./a
    
    0 讨论(0)
提交回复
热议问题