patterncsharpMinor
Basic, single-threaded implementation of SynchronizationContext
Viewed 0 times
singlesynchronizationcontextimplementationthreadedbasic
Problem
I am trying to write a
Edit: I see that I have forgotten to say - I need the message loop executions to be done a single, pre-determined thread. (In my case I want to push updates to an OpenGL context.)
Here is the class I have written:
```
public class SingleThreadedSynchronizationContext : SynchronizationContext
{
private sealed class WorkItem
{
private readonly SendOrPostCallback _callback;
private readonly object _state;
private readonly ManualResetEventSlim _reset;
public WorkItem (SendOrPostCallback callback, object state, ManualResetEventSlim reset)
{
if (callback == null)
throw new ArgumentNullException ("callback");
_callback = callback;
_state = state;
_reset = reset;
}
public void Execute ()
{
_callback (_state);
if (_reset != null) {
_reset.Set ();
}
}
}
private readonly ConcurrentQueue _workItems = new ConcurrentQueue ();
private readonly Thread _executingThread;
public SingleThreadedSynchronizationContext (Thread executingThread)
{
if (executingThread == null)
throw new ArgumentNullException ("executingThread");
_executingThread = executingThread;
}
internal bool HasWorkItems {
get {
return !_workItems.IsEmpty;
}
}
private WorkItem ExecuteAndReturnNextWorkItem ()
{
WorkItem currentItem;
if (_workItems.TryDequeue (out currentItem)) {
currentItem.Execute ();
}
return currentItem;
}
private void ExecuteNextWorkItem ()
{
ExecuteAndReturnNextWorkItem ();
}
public override void Post (SendOrPostCallback d, object state)
{
_workItems.Enqueue (new WorkItem (d, state, null));
}
SynchronizationContext in C# that represents a message queue, to be pumped from a main loop.Edit: I see that I have forgotten to say - I need the message loop executions to be done a single, pre-determined thread. (In my case I want to push updates to an OpenGL context.)
Here is the class I have written:
```
public class SingleThreadedSynchronizationContext : SynchronizationContext
{
private sealed class WorkItem
{
private readonly SendOrPostCallback _callback;
private readonly object _state;
private readonly ManualResetEventSlim _reset;
public WorkItem (SendOrPostCallback callback, object state, ManualResetEventSlim reset)
{
if (callback == null)
throw new ArgumentNullException ("callback");
_callback = callback;
_state = state;
_reset = reset;
}
public void Execute ()
{
_callback (_state);
if (_reset != null) {
_reset.Set ();
}
}
}
private readonly ConcurrentQueue _workItems = new ConcurrentQueue ();
private readonly Thread _executingThread;
public SingleThreadedSynchronizationContext (Thread executingThread)
{
if (executingThread == null)
throw new ArgumentNullException ("executingThread");
_executingThread = executingThread;
}
internal bool HasWorkItems {
get {
return !_workItems.IsEmpty;
}
}
private WorkItem ExecuteAndReturnNextWorkItem ()
{
WorkItem currentItem;
if (_workItems.TryDequeue (out currentItem)) {
currentItem.Execute ();
}
return currentItem;
}
private void ExecuteNextWorkItem ()
{
ExecuteAndReturnNextWorkItem ();
}
public override void Post (SendOrPostCallback d, object state)
{
_workItems.Enqueue (new WorkItem (d, state, null));
}
Solution
- You don't need to implement
Disposepattern if you have no unmanaged resources to dispose. Unless you get paid per line of code, that is :)
-
You should not dispose objects you did not create (unless you implement a wrapper). In your case, you should not dispose reset event in
WorkItem class, since you did not create it there. Instead, you should useusing(var reset = new ManualResetEventSlim())
{
_workItems.Enqueue (new WorkItem (d, state, reset));
...
}-
this code:
while (true)
{
synchronizationContext.Send(_ => Console.WriteLine("Hello!"), null)
while (synchronizationContext.HasWorkItems) {
synchronizationContext.ExecuteWorkItem ();
}
}will hang your application. You should either remove resetEvent from
Send method and simply execute all queued WorkItems there (bad approach) or move this loop:while (synchronizationContext.HasWorkItems) {
synchronizationContext.ExecuteWorkItem ();
}to sepatrate thread (better approach). Might look like:
public sealed class CustomSynchronizationContext : SynchronizationContext, IDisposable
{
public CustomSynchronizationContext()
{
_thread = new Thread(() =>
{
while(true)
{
_itemQueuedEvent.WaitOne();
if (_disposed) return;
ExecuteWorkItem();
}
});
_thread.Start();
}
........
}You will probably encounter some synchronization issues, but nothing that can not be solved.
Code Snippets
using(var reset = new ManualResetEventSlim())
{
_workItems.Enqueue (new WorkItem (d, state, reset));
...
}while (true)
{
synchronizationContext.Send(_ => Console.WriteLine("Hello!"), null)
while (synchronizationContext.HasWorkItems) {
synchronizationContext.ExecuteWorkItem ();
}
}while (synchronizationContext.HasWorkItems) {
synchronizationContext.ExecuteWorkItem ();
}public sealed class CustomSynchronizationContext : SynchronizationContext, IDisposable
{
public CustomSynchronizationContext()
{
_thread = new Thread(() =>
{
while(true)
{
_itemQueuedEvent.WaitOne();
if (_disposed) return;
ExecuteWorkItem();
}
});
_thread.Start();
}
........
}Context
StackExchange Code Review Q#31820, answer score: 3
Revisions (0)
No revisions yet.