A big thanks to @ Marc Gravell for his example. I was using a ConcurrentQueue<T> for cross-thread access between my library and Forms project.
Before seeing Marc's example, I was using a Timer to 'dequeue the queue' on the GUI thread. So following the example, I made a CustomConcurrentQueue<T> for thread safety. The CustomConcurrentQueue<T> also triggers an event when a new message is added to the Queue. The example below uses the event to add text to a textbox.
/// <summary>
/// Courtesy of Marc Gravell https://stackoverflow.com/questions/531438/c-triggering-an-event-when-an-object-is-added-to-a-queue
/// Allows event handling to notify if an object of type T has been enqueued or dequeued
/// </summary>
/// <typeparam name="T"></typeparam>
public class CustomConcurrentQueue<T>
{
private readonly ConcurrentQueue<T> queue = new ConcurrentQueue<T>();
public event EventHandler Changed;
public event EventHandler Enqueued;
protected virtual void OnChanged()
{
if (Changed != null) Changed(this, EventArgs.Empty);
}
protected virtual void OnEnqueued()
{
if (Enqueued != null) Enqueued(this, EventArgs.Empty);
}
public virtual void Enqueue(T item)
{
queue.Enqueue(item);
OnChanged();
OnEnqueued();
}
public int Count { get { return queue.Count; } }
public virtual bool TryDequeue(out T item)
{
if (queue.TryDequeue(out item))
{
OnChanged();
return true;
}
return false;
}
}
With an example of how I used it in a Form:
public CustomConcurrentQueue<string> MyMessages { get; set; }
MyMessages = new CustomConcurrentQueue<string>();
MyMessages.Enqueued += new new System.EventHandler(this.OnNewMessages);
//MyMessages.Enqueue("Hello World!");
private void OnNewMessages(object sender, EventArgs e)
{
if (MyMessages == null) return;
while (MyMessages.Count > 0)
{
var str = "";
if (MyMessages.TryDequeue(out str))
{
//Example: Append 'Hello World!' to the textbox on GUI thread...
Invoke((Action)(() =>
{
//this.textBox1.AppendText(Environment.NewLine + $"{str}");
}));
}
}
}