Introduction
Message-driven mechanism is the basis of the GUI system. One of the underlying infrastructure of message-driven is message queue, which is the hub of the entire GUI system, this article introduces a Message Queue Implementation Method Based on the ring queue, and provides its data structure, main operation flow and core code.
Ring queue
A loop queue is a data structure of a queue connected at the beginning and end. It follows the FIFO principle, as shown in:
In a circular queue, a set of sequential address storage units are used to store elements from the queue header to the end of the queue. The read and write positions are directed by the read_pos and write_pos pointers respectively.
When the queue is initialized, the value of read_pos = write_pos = 0. When a new element is written, the value of write_pos increases by 1. When an element is read, the value of read_pos increases by 1. If the queue is full, data cannot be written to the queue. If the queue is empty, data cannot be read. The method for determining whether the column is full is to check (write_pos + 1) % queue_size = read_pos is true, and the method for determining whether the queue is empty is to check whether write_pos = read_pos is true.
In view of the mutual exclusion and synchronization between threads when multiple threads access the ring queue at the same time, it is proposed to use a lock to control multiple threads to access the ring queue at the same time, and use a semaphore to control the synchronization between threads.
Within a period of time, only one thread can obtain the lock. When it holds the lock, other threads must wait to access the ring queue until the lock is released. As a result, the lock ensures that multiple threads access the circular queue mutex.
The thread first checks whether the semaphore is greater than 1 from the queue, and if so, reads data from the queue; otherwise, enters the waiting state until the semaphore is greater than 1; after a thread writes data to the queue, it will increase the semaphore by 1. If a thread is waiting, it will be awakened. As a result, semaphores implement synchronous access to the circular queue with multiple threads.
Flowchart
It is the main process of ring buffer initialization, Data Reading, and Data Writing.
- Allocate memory space for the Ring queue during initialization and complete the initialization of the lock and semaphore;
- If you write data to the ring queue, you must first obtain the lock. If the lock is occupied, it enters the waiting state. Otherwise, you can determine whether the ring queue is full. If the queue is full, the lock is released and returned. If the queue is not full, the data is written to the write_pos position, and the write_pos position is increased by 1. The lock is released and the semaphore is increased by 1, indicating that a data has been written;
- If data is read from the ring queue, first determine whether the semaphore is greater than 1. If not, wait. Otherwise, obtain the lock. If the lock is occupied, wait, otherwise, read data from read_pos, add read_pos to 1, release the lock, and read the data.
Data Structure
The data structure of the ring queue is as follows:
typedef _MSG { int message; void* param;} MSG;typedef _MSGQUE { pthread_mutex_t lock; sem_t wait; MSG* msg; int size; int read_ops; int write_ops;} MSGQUEUE;
The ring queue includes the following data:
- Lock: mutex lock;
- Wait: semaphore
- MSG: pointer to the data zone;
- Size: Maximum number of data in a ring queue;
- Read_ops: Read location;
- Write_ops: Write location.
Queue Initialization
Initialization mainly completes three tasks:
- Allocate memory for the Ring queue;
- Initialize the mutex lock and use pthread_mutex_init;
- Initialize the semaphore and use sem_init.
/* Create message queue */_msg_queue = malloc (sizeof (MSGQUEUE));/* init lock and sem */pthread_mutex_init (&_msg_queue->lock, NULL);sem_init (&_msg_queue->wait, 0, 0);/* allocate message memory */_msg_queue -> msg = malloc (sizeof(MSG) * nr_msg);_msg_queue -> size = nr_msg;
Write operation
As described in the flowchart above, write operations mainly include the following steps:-get the lock;
- Determines whether the queue is full;
- If it is not full, write the data to write_pos, add write_pos to 1, and determine whether write_pos is out of bounds;
- Release the lock and increase the semaphore by 1.
/* lock the message queue */pthread_mutex_lock (_msg_queue->lock);/* check if the queue is full. */if ((_msg_queue->write_pos + 1)% _msg_queue->size == _msg_queue->read_pos) { /* Message queue is full. */ pthread_mutex_unlock (_msg_queue->lock); return;}/* write a data to write_pos. */_msg_queue -> msg [write_pos] = *msg;write_pos ++;/* check if write_pos if overflow. */if (_msg_queue->write_pos >= _msg_queue->size) _msg_queue->write_pos = 0;/* release lock */pthread_mutex_unlock (_msg_queue->lock);sem_post (_msg_queue->wait);
Read operations
Similarly, read operations are divided into the following steps:
- Check semaphores;
- Obtain the lock;
- Determines whether the queue is empty;
- If it is not empty, read the data at read_ops, add read_ops to 1, and determine whether read_pos is out of bounds;
- And release the lock.
sem_wait (_msg_queue->wait);/* lock the message queue */pthread_mutex_lock (_msg_queue->lock);/* check if queue is empty */if (_msg_queue->read_pos != _msg_queue->write_pos) { msg = _msg_queue->msg + _msg_queue->read_pos;/* read a data and check if read_pos is overflow */ _msg_queue->read_pos ++; if (_msg_queue->read_pos >= _msg_queue->size) _msg_queue->read_pos = 0; return;}/* release lock*/pthread_mutex_unlock (_msg_queue->lock);
Problem
- The circular queue used in this article is of a fixed length and can be further improved to design a circular queue with a variable length;
- The message queue in this article is based on the "first-in-first-out" principle and does not consider messages with priority. However, this situation exists;
- This article focuses on the principles and implementation of message queues. For a GUI program, a message loop is also required to work with the message queue. The message loop will be separately summarized.
Http://www.linuxgraphics.cn/gui/message_queue.html