Producers and consumers mode
Code Description
Learn TDengine tsched.c code, which is more than producers and consumers of models, based on individual producers and consumers so busy waiting mode is not suitable, it will consume cpu resources, you must use the semaphore.
Specific ways:
- Two and a mutex semaphore.
- QueueMutex mutex used to protect data in the queue, producers and consumers need to acquire the mutex before accessing the queue.
- EmptySem semaphore indicates whether the queue empty position, ready for the new data.
- FullSem semaphore indicates whether there is data in the queue, it can be read.
- Each consumer and producer is a separate thread.
FIG Manufacturer processing cycle is as follows:
the consumer processing cycle is as follows:
the original code is a simple modification to the test code.
Test results
In order to be able to see the correct order of execution threads, can not directly use the printf function, so use a global character array record, add content to this global array in each thread when reading and writing data.
Complete test code
In linux run the test below.
-Lpthread need to add compiler options. Examples are as follows:
GCC -o producer_consumer producer_consumer.c -lpthread
#include <errno.h>
#include <pthread.h>
#include <semaphore.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <sys/syscall.h>
typedef struct
{
char data[16];
int num;
}SSchedMsg;
typedef struct
{
sem_t emptySem;
sem_t fullSem;
pthread_mutex_t queueMutex;
int fullSlot;
int emptySlot;
int queueSize;
int numOfThreads;
SSchedMsg * queue;
pthread_t * qthread;
} SSchedQueue;
#define RECORD_LEN 1024
static char record[RECORD_LEN];
static int record_len = RECORD_LEN-1;
static char * ptr;
static int done = 0;
void initRecord()
{
memset(record, 0, RECORD_LEN);
ptr = (char *)record;
}
/* type value 0:producer, 1:consumer */
void recordAction(int tid, int type, int num)
{
int len;
if(record_len <= 0)
{
done = 1;
return;
}
if(type == 0)
len = snprintf(ptr, record_len, "Producer[%d] set queue index[%d]\n",tid, num);
else
len = snprintf(ptr, record_len, "\t\tConsumer[%d] get queue index[%d]\n", tid, num);
record_len -= len;
ptr += len;
}
void * produceSchedQueue(void * param);
void * consumeSchedQueue(void *param);
void cleanupScheduler(void *param);
void * initScheduler(int queueSize, int numOfThreads)
{
int i = 0;
pthread_attr_t attr;
SSchedQueue * pSched = (SSchedQueue *)malloc(sizeof(SSchedQueue));
if(pSched == NULL) return NULL;
memset(pSched, 0, sizeof(SSchedQueue));
pSched->queueSize = queueSize;
pSched->numOfThreads = numOfThreads;
if(pthread_mutex_init(&pSched->queueMutex, NULL) < 0)
{
printf("init queueMutex failed, reason:%s", strerror(errno));
goto Error;
}
if(sem_init(&pSched->emptySem, 0, (unsigned int)pSched->queueSize) != 0)
{
printf("init empty semaphore failed, reason:%s", strerror(errno));
goto Error;
}
if(sem_init(&pSched->fullSem, 0, 0) != 0)
{
printf("init full semaphore failed, reason:%s", strerror(errno));
goto Error;
}
if((pSched->queue = (SSchedMsg *)malloc((size_t)pSched->queueSize * sizeof(SSchedMsg))) == NULL)
{
printf("no enough memory for queue, reason:%s", strerror(errno));
goto Error;
}
memset(pSched->queue, 0, (size_t)pSched->queueSize * sizeof(SSchedMsg));
pSched->fullSlot = 0;
pSched->emptySlot = 0;
pSched->qthread = malloc(sizeof(pthread_t) * (size_t)pSched->numOfThreads);
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
for(i = 0; i < pSched->numOfThreads; i++)
{
void * (*func)(void *);
if(i % 2 == 0)
func = consumeSchedQueue;
else
func = produceSchedQueue;
if(pthread_create(pSched->qthread + i, &attr, func, (void *)pSched) != 0)
{
printf("failed to create rpc thread[%d], reason:%s", i, strerror(errno));
goto Error;
}
}
printf("scheduler is initialized, numOfThreads:%d\n", pSched->numOfThreads);
return (void *)pSched;
Error:
cleanupScheduler((void *)pSched);
return NULL;
}
pid_t gettid()
{
return syscall(SYS_gettid);
}
void * consumeSchedQueue(void * param)
{
SSchedMsg msg;
SSchedQueue * pSched = (SSchedQueue *)param;
pid_t tid = gettid();
if(pSched == NULL)
{
printf("SSchedQueue is NULL");
return NULL;
}
while(1)
{
if(sem_wait(&pSched->fullSem) != 0)
{
printf("wait fullSem failed, errno:%d, reason:%s", errno, strerror(errno));
if(errno == EINTR)
continue;
}
if(pthread_mutex_lock(&pSched->queueMutex) != 0)
printf("lock queueMutex failed, reason:%s", strerror(errno));
msg = pSched->queue[pSched->fullSlot];
memset(pSched->queue + pSched->fullSlot, 0, sizeof(SSchedMsg));
pSched->fullSlot = (pSched->fullSlot + 1) % pSched->queueSize;
recordAction(tid, 1, msg.num);
if(pthread_mutex_unlock(&pSched->queueMutex) != 0)
printf("unlock queueMutex failed, reason:%s\n", strerror(errno));
if(sem_post(&pSched->emptySem) != 0)
printf("post emptySem failed, reason:%s\n", strerror(errno));
usleep(10000);
}
}
void * produceSchedQueue(void * param)
{
SSchedMsg msg;
SSchedQueue * pSched = (SSchedQueue *)param;
pid_t tid = gettid();
if(pSched == NULL)
{
printf("SSchedQueue is NULL");
return NULL;
}
memset(&msg, 0, sizeof(SSchedMsg));
while(1)
{
if(sem_wait(&pSched->emptySem) != 0)
{
printf("wait emptySem failed, errno:%d, reason:%s", errno, strerror(errno));
if(errno == EINTR)
continue;
}
if(pthread_mutex_lock(&pSched->queueMutex) != 0)
printf("lock queueMutex failed, reason:%s", strerror(errno));
msg.num = pSched->emptySlot;
pSched->queue[pSched->emptySlot] = msg;
pSched->emptySlot = (pSched->emptySlot + 1) % pSched->queueSize;
recordAction(tid, 0, msg.num);
if(pthread_mutex_unlock(&pSched->queueMutex) != 0)
printf("unlock queueMutex failed, reason:%s\n", strerror(errno));
if(sem_post(&pSched->fullSem) != 0)
printf("post fullSem failed, reason:%s\n", strerror(errno));
usleep(10000);
}
}
void cleanupScheduler(void * param)
{
int i = 0;
SSchedQueue * pSched = (SSchedQueue *)param;
if(pSched == NULL) return;
for( i = 0; i < pSched->numOfThreads; i++)
{
pthread_cancel(pSched->qthread[i]);
pthread_join(pSched->qthread[i], NULL);
}
sem_destroy(&pSched->emptySem);
sem_destroy(&pSched->fullSem);
pthread_mutex_destroy(&pSched->queueMutex);
free(pSched->qthread);
free(pSched->queue);
free(pSched);
}
int main()
{
initRecord();
initScheduler(6, 8);
while(1)
{
if(done)
{
printf("%s", record);
break;
}
}
}