docs: Updated README for event-queue design pattern (#2103)

* progress on the event-queue README.md, also added a new image resource.

* event-queue README.md complete - ready for final checks

* Update README.md
This commit is contained in:
SammanPali
2022-10-22 18:06:57 +11:00
committed by GitHub
parent 983d9e27e9
commit 36a51b5742
2 changed files with 178 additions and 7 deletions
+178 -7
View File
@@ -7,23 +7,194 @@ categories: Concurrency
language: en
tags:
- Game programming
- GUI
- code flow
---
## Intent
Event Queue is a good pattern if You have a limited accessibility resource (for example:
Audio or Database), but You need to handle all the requests that want to use that.
It puts all the requests in a queue and process them asynchronously.
Gives the resource for the event when it is the next in the queue and in same time
removes it from the queue.
The intent of the event queue design pattern, also known as message queues, is to decouple the relationship between the
sender and receiver of events within a system. By decoupling the two parties, they do not interact with the event queue
simultaneously. Essentially, the event queue handles and processes requests in an asynchronous manner, therefore, this
system can be described as a first in, first out design pattern model. Event Queue is a suitable pattern if there is a
resource with limited accessibility (i.e. Audio or Database), however, you need to provide access to all the requests
which seeks this resource. Upon accessing an event from the queue, the program also removes it from the queue.
![alt text](./etc/event-queue-model.png "Event Queue Visualised")
## Explanation
Real world example
> The modern emailing system is an example of the fundamental process behind the event-queue design pattern. When an email
> is sent, the sender continues their daily tasks without the necessity of an immediate response from the receiver.
> Additionally, the receiver has the freedom to access and process the email at their leisure. Therefore, this process
> decouples the sender and receiver so that they are not required to engage with the queue at the same time.
In plain words
> The buffer between sender and receiver improves maintainability and scalability of a system. Event queues are typically
> used to organise and carry out interprocess communication (IPC).
Wikipedia says
> Message queues (also known as event queues) implement an asynchronous communication pattern between two or more processes/
>threads whereby the sending and receiving party do not need to interact with the queue at the same time.
Key drawback
> As the event queue model decouples the sender-receiver relationship - this means that the event-queue design pattern is
> unsuitable for scenarios in which the sender requires a response. For example, this is a prominent feature within online
> multiplayer games, therefore, this approach require thorough consideration.
**Programmatic Example**
Upon examining our event-queue example, here's the app which utilised an event queue system.
```java
import javax.sound.sampled.UnsupportedAudioFileException;
import java.io.IOException;
public class App {
/**
* Program entry point.
*
* @param args command line args
* @throws IOException when there is a problem with the audio file loading
* @throws UnsupportedAudioFileException when the loaded audio file is unsupported
*/
public static void main(String[] args) throws UnsupportedAudioFileException, IOException,
InterruptedException {
var audio = Audio.getInstance();
audio.playSound(audio.getAudioStream("./etc/Bass-Drum-1.wav"), -10.0f);
audio.playSound(audio.getAudioStream("./etc/Closed-Hi-Hat-1.wav"), -8.0f);
LOGGER.info("Press Enter key to stop the program...");
try (var br = new BufferedReader(new InputStreamReader(System.in))) {
br.read();
}
audio.stopService();
}
}
```
Much of the design pattern is developed within the Audio class. Here we set instances, declare global variables and establish
the key methods used in the above runnable class.
```java
public class Audio {
private static final Audio INSTANCE = new Audio();
private static final int MAX_PENDING = 16;
private int headIndex;
private int tailIndex;
private volatile Thread updateThread = null;
private final PlayMessage[] pendingAudio = new PlayMessage[MAX_PENDING];
// Visible only for testing purposes
Audio() {
}
public static Audio getInstance() {
return INSTANCE;
}
}
```
The Audio class is also responsible for handling and setting the states of the thread, this is shown in the code segments
below.
```java
/**
* This method stops the Update Method's thread and waits till service stops.
*/
public synchronized void stopService() throws InterruptedException {
if (updateThread != null) {updateThread.interrupt();}
updateThread.join();
updateThread = null;
}
/**
* This method check the Update Method's thread is started.
* @return boolean
*/
public synchronized boolean isServiceRunning() {
return updateThread != null && updateThread.isAlive();}
/**
* Starts the thread for the Update Method pattern if it was not started previously. Also when the
* thread is ready it initializes the indexes of the queue
*/
public void init() {
if (updateThread == null) {
updateThread = new Thread(() -> {
while (!Thread.currentThread().isInterrupted()) {
update();
}});}
startThread();
}
/**
* This is a synchronized thread starter.
*/
private synchronized void startThread() {
if (!updateThread.isAlive()) {
updateThread.start();
headIndex = 0;
tailIndex = 0;
}
}
```
New audio is added into our event queue in the playSound method found in the Audio class. The update method is then utilised
to retrieve an audio item from the queue and play it to the user.
```java
public void playSound(AudioInputStream stream, float volume) {
init();
// Walk the pending requests.
for (var i = headIndex; i != tailIndex; i = (i + 1) % MAX_PENDING) {
var playMessage = getPendingAudio()[i];
if (playMessage.getStream() == stream) {
// Use the larger of the two volumes.
playMessage.setVolume(Math.max(volume, playMessage.getVolume()));
// Don't need to enqueue.
return;
}
}
getPendingAudio()[tailIndex] = new PlayMessage(stream, volume);
tailIndex = (tailIndex + 1) % MAX_PENDING;
}
```
Within the Audio class are some more methods with assist the construction of the event-queue design patterns, they are
summarised below.
- getAudioStream() = returns the input stream path of a file
- getPendingAudio() = returns the current event queue item
## Class diagram
![alt text](./etc/model.png "Event Queue")
## Applicability
Use the Event Queue pattern when
* You have a limited accessibility resource and the asynchronous process is acceptable to reach that
Use the Event Queue Pattern when
* The sender does not require a response from the receiver.
* You wish to decouple the sender & the receiver.
* You want to process events asynchronously.
* You have a limited accessibility resource and the asynchronous process is acceptable to reach that.
## Credits
* [Mihaly Kuprivecz - Event Queue] (http://gameprogrammingpatterns.com/event-queue.html)
* [Wikipedia - Message Queue] (https://en.wikipedia.org/wiki/Message_queue)
* [AWS - Message Queues] (https://aws.amazon.com/message-queue/)
Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB