Why does this not work?
What I am trying to do:
I need a way to run specific methods in a specific thread that lives on till the end of the program.
My other possible options:
As I understand a possible way to do it would be to implement a queue. Into which I could push in methods I want to be run in the specific thread. In the specific thread, I would be spinning and sleeping / monitor.pulse to see if there are delegates waiting to be run in the queue.
My objective:
Is to avoid all the hardwork to create the delegate queue, maintain lock etc. It appears that a ready made solution exists in the WPF world called Dispatcher. WPF controls mostly inherit from DispatcherObject and somehow the whole thing works. What do I have to do to get this work?
using System;
using System.Threading;
using System.Windows.Threading;
namespace ThreadingTrials
{
class Program
{
[STAThread]
static void Main(string[] args)
{
Thread.CurrentThread.Name = "mainThread";
Engine engine = new Engine();
Console.WriteLine("initializing SpecialEngine from {0}", Thread.CurrentThread.Name);
engine.initialize();
engine.doWork();
}
}
class Engine:DispatcherObject
{
private EventWaitHandle InitializationComplete;
private EventWaitHandle newWorkComplete;
//private Dispatcher dispatcher;
public Engine()
{
}
public void initialize()
{
InitializationComplete = new EventWaitHandle(false, EventResetMode.ManualReset);
Thread thread = new Thread(new ParameterizedThreadStart((hwnd)=>
{
InitializeSpecialEngineObject();
while (true) ;
}));
thread.Name = "Special Engine Thread";
thread.SetApartmentState(ApartmentState.STA);
thread.Priority = ThreadPriority.Normal;
thread.Start();
Console.WriteLine("waiting for initialize at {0}", Thread.CurrentThread.Name);
InitializationComplete.WaitOne();
}
private void InitializeSpecialEngineObject()
{
Console.WriteLine("doing initialization at {0}", Thread.CurrentThread.Name);
Thread.Sleep(500);
//dispatcher = Dispatcher.CurrentDispatcher;
InitializationComplete.Set();
}
internal void doWork()
{
newWorkComplete = new EventWaitHandle(false, EventResetMode.AutoReset);
//Dispatcher.Thread.Suspend();
Dispatcher.Invoke((SendOrPostCallback)delegate
{
Console.WriteLine("dispatched to {0}", Thread.CurrentThread.Name);
Thread.Sleep(500);
newWorkComplete.Set();
},DispatcherPriority.Background, null);
Dispatcher.Thread.Resume();
Console.WriteLine("waiting for new work to complete at {0}", Thread.CurrentThread.Name);
newWorkComplete.WaitOne();
}
private void doingWork()
{
Console.WriteLine("Doing work in {0}", Thread.CurrentThread.Name);
Thread.Sleep(500);
}
}
}
Thanks for the input.
Fair enough. It was actually very little work to make a simple worker thread that waits for an event signifying a new task in queue of void() delegates, and runs them as they become available. I copied most of the code from a website online… Sorry lost reference. I had done it on that day, and should have edited this post earlier.
using System;
using System.Threading;
using System.Collections.Generic;
class ProducerConsumerQueue : IDisposable
{
EventWaitHandle _wh = new AutoResetEvent(false);
Thread _worker;
readonly object _locker = new object();
Queue<Action> _tasks = new Queue<Action>();
public delegate void Action();
public ProducerConsumerQueue()
{
_worker = new Thread(Work);
_worker.Start();
}
public void EnqueueTask(Action work)
{
lock (_locker) _tasks.Enqueue(work);
_wh.Set();
}
public void Dispose()
{
EnqueueTask(null); // Signal the consumer to exit.
_worker.Join(); // Wait for the consumer's thread to finish.
_wh.Close(); // Release any OS resources.
}
void Work()
{
while (true)
{
Action task = null;
lock (_locker)
if (_tasks.Count > 0)
{
task = _tasks.Dequeue();
if (task == null) return;
}
if (task != null)
{
task.Invoke();
}
else
_wh.WaitOne(); // No more tasks - wait for a signal
}
}
}
class Program
{
static void Main()
{
using (ProducerConsumerQueue q = new ProducerConsumerQueue())
{
q.EnqueueTask(delegate
{
Console.WriteLine("Performing task: Hello");
Thread.Sleep(1000); // simulate work...
});
for (int i = 0; i < 10; i++) q.EnqueueTask(delegate
{
Console.WriteLine("Performing task: "+ i);
Thread.Sleep(1000); // simulate work...
});
q.EnqueueTask(delegate
{
Console.WriteLine("Performing task: Goodbye!");
Thread.Sleep(1000); // simulate work...
});
}
// Exiting the using statement calls q's Dispose method, which
// enqueues a null task and waits until the consumer finishes.
}
}
You don’t call
Dispatcher.Run. You’re assuming that aDispatchercontains aThreadthat does its work, but this is backwards. The first time thatDispatcher.CurrentDispatcheris called, aDispatcheris created bound to the current thread. Note that even though your code doesn’t callCurrentDispatcherdirectly, it does do so indirectly by constructing aDispatcherObject(which capturesCurrentDispatcherinto a field).Review the WPF threading model docs, which contain all the details you need.
If you want dispatcher-like functionality in a child thread but don’t want to take a dependency on WPF, you can use the
ActionThreadclass from Nito.Async, which is roughly equivalent to aDispatcherplus a dedicatedThread.