是否可以从父线程访问/更新子线程的资源? [英] Is it possible to access/update the child thread's resource from parent thread?

查看:61
本文介绍了是否可以从父线程访问/更新子线程的资源?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在用C语言进行套接字编程,对Multithreading还是完全陌生的.

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..) and enqueue the same to the queue of its child thread.The problem here is, how can I update the queue which is child thread-specific from the parent thread.

推荐答案

支持多个生产者和消费者的线程安全队列.

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(示例用户):

#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;
}

如何运行示例用户:

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 -lpthread && ./a

这篇关于是否可以从父线程访问/更新子线程的资源?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆