TDengine Code learning (1): Producer and consumer mode

Disclaimer: This article is a blogger original article, follow the CC 4.0 BY-SA copyright agreement, reproduced, please attach the original source link and this statement.
This link: https://blog.csdn.net/marble_xu/article/details/98197962

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:
Producers
the consumer processing cycle is as follows:
consumer
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.
Here Insert Picture Description

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

Guess you like

Origin blog.csdn.net/marble_xu/article/details/98197962