I'm trying to create a second message loop to process/filter low level messages asynchronously in C#. It works by creating a hidden form, exposing it's Handle property to be hooked, and run a second message loop in a separate thread. At the moment I'm quite happy with the results but I'm unable to exit from the second loop properly. The only workaround was setting the IsBackground property to true, so the second thread will be simply terminated (without processing all the pending messages) at main application exit.
The question is: how to proper quit that message loop so the second Application.Run() returns? I tried differents approaches creating a separate ApplicationContext and controlling various events (Application.ApplicationExit, Application.ThreadExit, ApplicationContext.ThreadExit) but they all failed with race conditions I'm unable to debug.
Any hint? Thanks
This is the code:
public class MessagePump
{
public delegate void HandleHelper(IntPtr handle);
public MessagePump(HandleHelper handleHelper, Filter filter)
{
Thread thread = new Thread(delegate()
{
ApplicationContext applicationContext = new ApplicationContext();
Form form = new Form();
handleHelper(form.Handle);
Application.AddMessageFilter(new MessageFilter(filter));
Application.Run(applicationContext);
});
thread.SetApartmentState(ApartmentState.STA);
thread.IsBackground = true; // <-- The workaround
thread.Start();
}
}
public delegate bool Filter(ref Message m);
internal class MessageFilter : IMessageFilter
{
private Filter _Filter;
public MessageFilter(Filter filter)
{
_Filter = filter;
}
#region IMessageFilter Members
public bool PreFilterMessage(ref Message m)
{
return _Filter(ref m);
}
#endregion // IMessageFilter Members
}
I use it in the main Form constructor in this way:
_Completion = new ManualResetEvent(false);
MessagePump pump = new MessagePump(
delegate(IntPtr handle)
{
// Sample code, I did this form twain drivers low level wrapping
_Scanner = new TwainSM(handle);
_Scanner.LoadDs("EPSON Perfection V30/V300");
},
delegate(ref Message m)
{
// Asyncrhronous processing of the messages
// When the correct message is found -->
_Completion.Set();
}
EDIT: Full solution in my answer.
You should pass the Form instance to the ApplicationContext ctor as a parameter:
applicationContext = new ApplicationContext(form);
Right now, you are basically creating a no-context instance, which doesn't care about your form being closed.
Also, it is a good practice to do some cleanup, like removing the filter when you don't need it anymore:
Form form = new Form();
ApplicationContext applicationContext = new ApplicationContext(form);
handleHelper(form.Handle);
MessageFilter filter = new MessageFilter(filter);
Application.AddMessageFilter(filter);
Application.Run(applicationContext);
Application.RemoveMessageFilter(filter);
[Edit]
If you don't want to show the form, then you can use the paramaterless ctor, but you will have to close the context manually by calling the ApplicationContext.ExitThread method. This method actually gets called when your form fires the FormClosed event, if you pass the form in the constructor.
Since hidden form is not related to the context, you need to exit them both at some time.
I eventually realized that the thread.IsBackground = true; was not bad, because it was the only way to determine "hey, I'm the last thread running, I'm supposed to quit". Correct resource cleaning is still needed, tough. For this, a third delegate for resource cleaning is needed and I just registered it to the AppDomain.CurrentDomain.ProcessExit event. I even provided a ExitLoop() method to the MessageLoop class (was MessagePump in the question). In this way, I can terminate the message loop anytime. Critical sections of ExitLoop() and ProcessExit handler are mutexed.
The code:
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Threading;
namespace System
{
public class MessageLoop
{
#region Fields
private Object _Lock;
private ApplicationContext _ApplicationContext;
private CustomMessageFilter _MessageFilter;
private HandleProvider _ResourceCleaner;
private ManualResetEvent _Completion;
private bool _Disposed;
#endregion // Fields
#region Constructors
/// <summary>
/// Run a second message pump that will filter messages asyncronously
/// </summary>
/// <param name="provideHandle">A delegate that provide a window handle for
/// resource initializing</param>
/// <param name="messageFilter">A delegate for message filtering</param>
/// <param name="cleanResources">A delegate for proper resource cleaning
/// before quitting the loop</param>
/// <param name="background">State if the loop should be run on a background
/// thread or not. If background = false, please be aware of the
/// possible race conditions on application shut-down.</param>
public MessageLoop(HandleProvider initializeResources, MessageFilter messageFilter,
HandleProvider cleanResources, bool background)
{
_Lock = new Object();
_ResourceCleaner = cleanResources;
_Completion = new ManualResetEvent(false);
_Disposed = false;
Thread thread = new Thread(delegate()
{
_ApplicationContext = new ApplicationContext();
WindowHandle window = new WindowHandle();
initializeResources(window.Handle);
_MessageFilter = new CustomMessageFilter(messageFilter);
Application.AddMessageFilter(_MessageFilter);
// Signal resources initalizated
_Completion.Set();
// If background = true, do resource cleaning on ProcessExit event
if (background)
{
AppDomain.CurrentDomain.ProcessExit +=
new EventHandler(CurrentDomain_ProcessExit);
}
// Run the message loop
Application.Run(_ApplicationContext);
// Clean resource before leaving the thread
cleanResources(window.Handle);
// Signal resources cleaned
_Completion.Set();
});
thread.SetApartmentState(ApartmentState.STA);
thread.IsBackground = background;
thread.Start();
// Before returning the instace, wait for thread resources initialization
_Completion.WaitOne();
}
#endregion // Constructors
#region Inquiry
/// <summary>
/// Early exit the message loop
/// </summary>
public void ExitLoop()
{
lock (_Lock)
{
if (_Disposed)
return;
// Completion was already signaled in the constructor
_Completion.Reset();
// Tell the message loop thread to quit
_ApplicationContext.ExitThread();
// Wait for thread resources cleaning
_Completion.WaitOne();
_Disposed = true;
}
}
#endregion // Inquiry
#region Event handlers
void CurrentDomain_ProcessExit(object sender, EventArgs e)
{
lock (_Lock)
{
if (_Disposed)
return;
// Completion was already signaled in the constructor
_Completion.Reset();
// Tell the message loop thread to quit
_ApplicationContext.ExitThread();
// Wait for thread resources cleaning
_Completion.WaitOne();
_Disposed = true;
}
}
#endregion // Event handlers
#region Support
public delegate void HandleProvider(IntPtr handle);
public delegate bool MessageFilter(ref Message m);
internal class CustomMessageFilter : IMessageFilter
{
private MessageFilter _Filter;
public CustomMessageFilter(MessageFilter filter)
{
_Filter = filter;
}
#region IMessageFilter Members
public bool PreFilterMessage(ref Message m)
{
return _Filter(ref m);
}
#endregion // IMessageFilter Members
}
#endregion // Support
}
public class WindowHandle : NativeWindow
{
public WindowHandle()
{
CreateParams parms = new CreateParams();
CreateHandle(parms);
}
~WindowHandle()
{
DestroyHandle();
}
}
}
Can be used this way:
_Completion = new ManualResetEvent(false);
MessageLoop messageLoop = new MessageLoop(
delegate(IntPtr handle) // Resource initializing
{
// Sample code, I did this form twain drivers low level wrapping
_Scanner = new TwainSM(handle);
_Scanner.LoadDs("EPSON Perfection V30/V300");
},
delegate(ref Message m) // Message filtering
{
// Asyncrhronous processing of the messages
// When the correct message is found -->
_Completion.Set();
},
delegate(IntPtr handle) // Resource cleaning
{
// Resource cleaning/disposing. In my case, it's the following...
_Scanner.Dispose();
}, true); // Automatically quit on main application shut-down
// Anytime you can exit the loop
messageLoop.ExitLoop();
Related
I would like to use Condition Variable in order to know when Messages Queue is not empty, i would like to use it in "HandleMessageQueue" as a thread
private static Queue<Message> messages = new Queue<Message>();
/// <summary>
/// function return the first message
/// </summary>
/// <returns>first message element</returns>
public static Message GetFirst()
{
return messages.Dequeue();
}
in another class:
/// <summary>
/// Function run while the clients connected and handle the queue message
/// </summary>
public static void HandleMessageQueue()
{
// ...
}
What you're probably looking for is a simple producer-consumer pattern. In this case I'd recommend using .NET's BlockingCollection, which allows you to easily handle the following cases:
have one thread push stuff in a queue
have another thread block until stuff is available
make the whole thing easy to shutdown without having to forcibly terminate the thread
Here's a short code sample, read the comments for more information about what every bit does:
public class Queue : IDisposable
{
private readonly Thread _messageThread; // thread for processing messages
private readonly BlockingCollection<Message> _messages; // queue for messages
private readonly CancellationTokenSource _cancellation; // used to abort the processing when we're done
// initializes everything and starts a processing thread
public Queue()
{
_messages = new BlockingCollection<Message>();
_cancellation = new CancellationTokenSource();
_messageThread = new Thread(ProcessMessages);
_messageThread.Start();
}
// processing thread function
private void ProcessMessages()
{
try
{
while (!_cancellation.IsCancellationRequested)
{
// Take() blocks until either:
// 1) a message is available, in which case it returns it, or
// 2) the cancellation token is cancelled, in which case it throws an OperationCanceledException
var message = _messages.Take(_cancellation.Token);
// process the message here
}
}
catch (OperationCanceledException)
{
// Take() was cancelled, let the thread exit
}
}
// pushes a message
public void QueueMessage(Message message)
{
_messages.Add(message);
}
// stops processing and clean up resources
public void Dispose()
{
_cancellation.Cancel(); // let Take() abort by throwing
_messageThread.Join(); // wait for thread to exit
_cancellation.Dispose(); // release the cancellation source
_messages.Dispose(); // release the queue
}
}
Another option would be to combine a ConcurrentQueue<T> with a ManualResetEvent (events are roughly the .NET equivalent to condition variables), but that would be doing by hand what BlockingCollection<T> does).
something like this?
public class EventArgs<T> : EventArgs
{
private T eventData;
public EventArgs(T eventData)
{
this.eventData = eventData;
}
public T EventData
{
get { return eventData; }
}
}
public class ObservableQueue<T>
{
public event EventHandler<EventArgs<T>> EnQueued;
public event EventHandler<EventArgs<T>> DeQueued;
public int Count { get { return queue.Count; } }
private readonly Queue<T> queue = new Queue<T>();
protected virtual void OnEnqueued(T item)
{
if (EnQueued != null)
EnQueued(this, new EventArgs<T>(item));
}
protected virtual void OnDequeued(T item)
{
if (DeQueued != null)
DeQueued(this, new EventArgs<T>(item));
}
public virtual void Enqueue(T item)
{
queue.Enqueue(item);
OnEnqueued(item);
}
public virtual T Dequeue()
{
var item = queue.Dequeue();
OnDequeued(item);
return item;
}
}
and use it
static void Main(string[] args)
{
ObservableQueue<string> observableQueue = new ObservableQueue<string>();
observableQueue.EnQueued += ObservableQueue_EnQueued;
observableQueue.DeQueued += ObservableQueue_DeQueued;
observableQueue.Enqueue("abc");
observableQueue.Dequeue();
Console.Read();
}
I have multi-threaded application, where different threads may want to perform an operation. I tried to use Mutex to make sure, that thread does not start an operation if it is already running.
System.Threading.Mutex mutex;
bool isRunning = System.Threading.Mutex.TryOpenExisting(name, out mutex);
if (!isRunning)
{
RunMethod();
}
within method I created mutex, and try to release it at the end:
var mutex = new Mutex(true, name);
try{
//do stuff, it takes some time
}
finally
{
//TODO: I want to get rid of Mutex here
}
How do I get rid of mutex? Because even after I called mutex.ReleaseMutex() and mutex.Close(), it still exists and can be found. How can I inform that operation is currently running or finished?
Is there another way to do this?
Same, like CodingGorilla said, using events is easier.
I hope I understand your question well.
This example shows some events techniques:
Waiting for a thread has been started.
The use of waiting on multiple events (WaitHandle.WaitAny())
How to terminate a thread, safe.
Testing an event state without waiting (.WaitOne(0))
Here is an example:
public class MultiThreadedExample : IDisposable
{
private Thread _thread;
private ManualResetEvent _terminatingEvent = new ManualResetEvent(false);
private ManualResetEvent _runningEvent = new ManualResetEvent(false);
private ManualResetEvent _threadStartedEvent = new ManualResetEvent(false);
public MultiThreadedExample()
{
_thread = new Thread(MyThreadMethod);
_thread.Start();
_threadStartedEvent.WaitOne();
}
private void MyThreadMethod()
{
_threadStartedEvent.Set();
var events = new WaitHandle[] { _terminatingEvent, _runningEvent };
while (WaitHandle.WaitAny(events) != 0) // <- WaitAny returns index within the array of the event that was Set.
{
try
{
// do work......
}
finally
{
// reset the event. so it can be triggered again.
_runningEvent.Reset();
}
}
}
public bool TryStartWork()
{
// .Set() will return if the event was set.
return _runningEvent.Set();
}
public bool IsRunning
{
get { return _runningEvent.WaitOne(0); }
}
public void Dispose()
{
// break the whileloop
_terminatingEvent.Set();
// wait for the thread to terminate.
_thread.Join();
}
}
I want to make a render loop to render on a WPF Window or a WinForm. Therefore I want to use SharpGL (https://sharpgl.codeplex.com/). To make my loop I made a thread:
public void Run()
{
IsRunning = true;
this.Initialize();
while (IsRunning)
{
Render(/* arguments here */);
// pausing and stuff
}
Dispose();
}
In Render I want to send the Draw Calls to the GPU. So far there is no problem. But Winforms and WPF need their own thread and loop. So I can't just create a Window and draw onto like in Java with LWJGL (https://www.lwjgl.org/), which I used before. I have to start another thread, that runs the Form in an Application (I cut out error handling to make it short):
[STAThread]
private void HostWinFormsApplication()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(true);
// get Display
Display = Engine.MainModule.Resolve<IDisplay>();
Display.Initialize();
Display.Closing += (s, e) => Stop();
Application.Run(Display as Form);
}
When my Renderer wants to access the OpenGL-Control on my Form and use it, an error occurs, as WinForms (and WPF) don't want their Controls to be manipulated by other Threads. So maybe an Invoke is an option, but this would delay my drawcalls and become a bottleneck.
A timer isn't an option, too, as it isn't accurate and unflexible... And I simply don't like it.
And doing everything inside the Window Code may be possible, but I want an application being independent of its Display, so that it can be changed. It should be an application having a Display not a Display running an application.
In LWJGL I just had the possibility to create and initialize a Display and then simply use it. The only thing to consider was updating it and everything went fine.
So I just want to create a Window in my render thread and draw onto. If I do it this way, the window just gets unusable and greyish as it needs this .Net-Loop. Is there any possibility to realize that or does anybody know another way to create Windows? Can I handle the window loop manually?
Any idea is welcome.
If there would be a way to do this with a WPF Window it would be awesome. Then I could have an OpenGL Control and all WPF-Stuff to make an Editor!
The solution was to call
Application.DoEvents();
in my Render Loop manually, as someone told me. So I didn't had to use Application.Run and were able to use my Form in the thread of the loop:
public void Run()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(true);
IsRunning = true;
this.Initialize();
MyForm = new CoolRenderForm();
MyForm.Show();
while (IsRunning)
{
Render(/* arguments here */);
Application.DoEvents();
// refresh form
// pausing and stuff
}
Dispose();
}
Based on my question: How to make a render loop in WPF?
WPF render loop
The best way to do this is to use the per-frame callbacks provided by the static
CompositionTarget.Rendering event.
WinForms render loop
Below is the code of a WinForms render loop class that I made based on this blog post:
Just use:
WinFormsAppIdleHandler.Instance.ApplicationLoopDoWork += Instance_ApplicationLoopDoWork;
WinFormsAppIdleHandler.Instance.Enabled = true;
Class:
using System;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;
namespace Utilities.UI
{
/// <summary>
/// WinFormsAppIdleHandler implements a WinForms Render Loop (max FPS possible).
/// Reference: http://blogs.msdn.com/b/tmiller/archive/2005/05/05/415008.aspx
/// </summary>
public sealed class WinFormsAppIdleHandler
{
private readonly object _completedEventLock = new object();
private event EventHandler _applicationLoopDoWork;
//PRIVATE Constructor
private WinFormsAppIdleHandler()
{
Enabled = false;
SleepTime = 5; //You can play/test with this value
Application.Idle += Application_Idle;
}
/// <summary>
/// Singleton from:
/// http://csharpindepth.com/Articles/General/Singleton.aspx
/// </summary>
private static readonly Lazy<WinFormsAppIdleHandler> lazy = new Lazy<WinFormsAppIdleHandler>(() => new WinFormsAppIdleHandler());
public static WinFormsAppIdleHandler Instance { get { return lazy.Value; } }
/// <summary>
/// Gets or sets if must fire ApplicationLoopDoWork event.
/// </summary>
public bool Enabled { get; set; }
/// <summary>
/// Gets or sets the minimum time betwen ApplicationLoopDoWork fires.
/// </summary>
public int SleepTime { get; set; }
/// <summary>
/// Fires while the UI is free to work. Sleeps for "SleepTime" ms.
/// </summary>
public event EventHandler ApplicationLoopDoWork
{
//Reason of using locks:
//http://stackoverflow.com/questions/1037811/c-thread-safe-events
add
{
lock (_completedEventLock)
_applicationLoopDoWork += value;
}
remove
{
lock (_completedEventLock)
_applicationLoopDoWork -= value;
}
}
/// <summary>
/// FINALMENTE! Imagem ao vivo sem travar! Muito bom!
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Application_Idle(object sender, EventArgs e)
{
//Try to update interface
while (Enabled && IsAppStillIdle())
{
OnApplicationIdleDoWork(EventArgs.Empty);
//Give a break to the processor... :)
//8 ms -> 125 Hz
//10 ms -> 100 Hz
Thread.Sleep(SleepTime);
}
}
private void OnApplicationIdleDoWork(EventArgs e)
{
var handler = _applicationLoopDoWork;
if (handler != null)
{
handler(this, e);
}
}
/// <summary>
/// Gets if the app still idle.
/// </summary>
/// <returns></returns>
private static bool IsAppStillIdle()
{
bool stillIdle = false;
try
{
Message msg;
stillIdle = !PeekMessage(out msg, IntPtr.Zero, 0, 0, 0);
}
catch (Exception e)
{
//Should never get here... I hope...
MessageBox.Show("IsAppStillIdle() Exception. Message: " + e.Message);
}
return stillIdle;
}
#region Unmanaged Get PeekMessage
// http://blogs.msdn.com/b/tmiller/archive/2005/05/05/415008.aspx
[System.Security.SuppressUnmanagedCodeSecurity] // We won't use this maliciously
[DllImport("User32.dll", CharSet = CharSet.Auto)]
public static extern bool PeekMessage(out Message msg, IntPtr hWnd, uint messageFilterMin, uint messageFilterMax, uint flags);
#endregion
}
}
I have a class that uses BlockingCollection like this:
public class Logger : IDisposable
{
private BlockingCollection<LogMessage> _messages = null;
private Thread _worker = null;
private bool _started = false;
public void Start()
{
if (_started) return;
//Some logic to open log file
OpenLogFile();
_messages = new BlockingCollection<LogMessage>(); //int.MaxValue is the default upper-bound
_worker = new Thread(Work) { IsBackground = true };
_worker.Start();
_started = true;
}
public void Stop()
{
if (!_started) return;
// prohibit adding new messages to the queue,
// and cause TryTake to return false when the queue becomes empty.
_messages.CompleteAdding();
// Wait for the consumer's thread to finish.
_worker.Join();
//Dispose managed resources
_worker.Dispose();
_messages.Dispose();
//Some logic to close log file
CloseLogFile();
_started = false;
}
/// <summary>
/// Implements IDiposable
/// In this case, it is simply an alias for Stop()
/// </summary>
void IDisposable.Dispose()
{
Stop();
}
/// <summary>
/// This is message consumer thread
/// </summary>
private void Work()
{
LogMessage message;
//Try to get data from queue
while(_messages.TryTake(out message, Timeout.Infinite))
WriteLogMessage(message); //... some simple logic to write 'message'
}
}
I create a new instance of the Logger class, call its Start() method. Then, if I forget to call the Dispose method when the instance is no longer referenced, then the Worker Thread will never end. That's a kind of memory leak. Am I right? and how to overcome this?
You may try to keep only a weak reference to the BlockingCollection in your worker thread and do not reference an object that is referencing the BlockingCollection. I made a static method to ensure that we don't reference the Logger instance this.
This way the collection can be finalized/collected when it is no longer referenced. I'm not sure it'll work, you have to try, It depends on whether TryTake keeps the collection alive or not. It may not work in debug as the GC behave differently, so try it in release without debugger attached.
public class Logger : IDisposable
{
private BlockingCollection<LogMessage> _messages = null;
private Thread _worker = null;
private bool _started = false;
public void Start()
{
if (_started) return;
//Some logic to open log file
OpenLogFile();
_messages = new BlockingCollection<LogMessage>(); //int.MaxValue is the default upper-bound
_worker = new Thread(Work) { IsBackground = true };
_worker.Start(new WeakReference<BlockingCollection<LogMessage>>(_messages));
_started = true;
}
public void Stop()
{
if (!_started) return;
// prohibit adding new messages to the queue,
// and cause TryTake to return false when the queue becomes empty.
_messages.CompleteAdding();
// Wait for the consumer's thread to finish.
_worker.Join();
//Dispose managed resources
_worker.Dispose();
_messages.Dispose();
//Some logic to close log file
CloseLogFile();
_started = false;
}
/// <summary>
/// Implements IDiposable
/// In this case, it is simply an alias for Stop()
/// </summary>
void IDisposable.Dispose()
{
Stop();
}
/// <summary>
/// This is message consumer thread
/// </summary>
private static void Work(object state)
{
LogMessage message;
//Try to get data from queue
do
{
BlockingCollection<LogMessage> messages;
if (((WeakReference<BlockingCollection<LogMessage>>)state).TryGetTarget(out messages)
&& messages.TryTake(out message, Timeout.Infinite))
{
WriteLogMessage(message); //... some simple logic to write 'message'
continue;
}
} while (false);
}
}
I have a service that is running constantly processing data, it receives requests to process new data through messaging. While it's busy processing new requests get merged together so that they are then all processed at once. An AutoResetEvent is used to notify the processor that a new request is available.
My question is in EventLoop should it be possible that currentRequest after the WaitOne to be null?
Is it bad practice to have the _eventAvailable.Set() outside of the lock(_eventLocker)? I moved it out so that it wouldn't start going at the WaitOne and immediately contest the lock(_eventLocker).
Any suggestions on how to better write the following code?
public sealed class RealtimeRunner : MarshalByRefObject
{
/// <summary>
/// The actual event, new events get merged into this if it is not null
/// </summary>
private Request _pendingRequest;
/// <summary>
/// Used to signal the runner thread when an event is available to process
/// </summary>
private readonly AutoResetEvent _eventAvailable = new AutoResetEvent(false);
private readonly object _eventLocker = new object();
/// <summary>
/// Called on a background thread via messaging
/// </summary>
public void QueueEvent(RealtimeProcessorMessage newRequest)
{
bool mergedRequest;
lock (_eventLocker)
{
if (_pendingRequest == null)
{
mergedRequest = false;
_pendingRequest = new Request(newRequest, _engine);
}
else
{
mergedRequest = true;
_pendingRequest.Merge(newRequest, _engine);
}
}
_eventAvailable.Set();
}
/// <summary>
/// This is running on its own thread
/// </summary>
private void EventLoop()
{
while (true)
{
// Block until something exists in _pendingRequest
_eventAvailable.WaitOne();
Request currentRequest;
lock (_eventLocker)
{
currentRequest = _pendingRequest;
_pendingRequest = null;
}
// CAN THIS EVER BE NULL?
if (currentRequest == null)
continue;
//do stuff with the currentRequest here
}
}
}
Yes, the if (currrentRequest == null) could evaluate to true. Consider two threads racing to call _eventAvailable.Set(). One completes the call and the other gets preempted. Meanwhile the EventLoop thread wakes up and completes an entire iteration of the loop. You now have a situation where _pendingRequest is null and the WaitHandle is still waiting to be signaled again.
I want to present an entirely different solution to the problem. It looks your code could be simplified by using the producer-consumer pattern. This pattern is most easily implemented using a blocking queue. The BlockingCollection class implements such a queue.
public sealed class RealtimeRunner : MarshalByRefObject
{
private BlockingCollection<Request> m_Queue = new BlockingCollection<Request>();
public void QueueEvent(RealtimeProcessorMessage newRequest)
{
m_Queue.Add(new Request(newRequest _engine));
}
private void EventLoop()
{
while (true)
{
// This blocks until an item appears in the queue.
Request request = m_Queue.Take();
// Process the request here.
}
}
}