Mono High Resolution timer (on Linux) - c#

I am porting a windows C# application that polls at 50ms (for serial comms) to Linux (using Mono). We are currently using the ZylTimer (by ZylSoft) to generate "tick" events at each interval , however as this library wraps pInvoke calls to the windows multimedia library, we of course cannot use this.
//i.e.
timZylComms.Tick += new ZylTimer.TickEventHandler(timZylComms_Tick);
timTimeout.Tick += new ZylTimer.TickEventHandler(timTimeout_Tick);
So, this leads me to ask if either there exists an alternative I can use under Mono?
Would the best approach be to extend the "Stopwatch" class (which counts at a high resolution) with a Tick event?
Or are there any linux libraries I can wrap to reproduce this functionality?
Or is there some other way of achieving this?
Appreciate any thoughts on this.
EDIT:
Would there be any problems with going with this:
internal class LinuxHiResTimer{
internal event EventHandler Tick;
private System.Diagnostics.Stopwatch watch;
internal int Interval{ get; set;}
private bool enabled;
internal bool Enabled {
get{ return enabled; }
set {
if (value) {
watch.Start ();
Task.Run (tickGenerator);
enabled = value;
} else {
enabled = value;
}
}
}
private async Task tickGenerator(){
while (enabled){
if (watch.ElapsedMilliseconds > Interval) {
watch.Reset ();
if (Tick != null)
Tick (this, new EventArgs ());
} else {
float fWaitPeriod = (float)(0.8 * (Interval - watch.ElapsedMilliseconds));
if (fWaitPeriod>20)
await Task.Delay(TimeSpan.FromMilliseconds(fWaitPeriod));
}
}
watch.Stop ();
}
internal LinuxHiResTimer(){
watch = new Stopwatch ();
}
~LinuxHiResTimer(){
watch.Stop ();
}
}

This is what I have now.
It does the job (tested with generating ticks at 25ms).
It works by using nanosleep() (through the Mono.Unix.Native wrapper), and I would like to share this with others in case they are looking to implement something similar.
using Mono.Unix.Native;
namespace drone.StackOverflow{
internal class LinuxHiResTimer {
internal event EventHandler Tick; // Tick event
private System.Diagnostics.Stopwatch watch; // High resolution time
const uint safeDelay = 0; // millisecond (for slightly early wakeup)
private Timespec pendingNanosleepParams = new Timespec();
private Timespec threadNanosleepParams = new Timespec();
object lockObject = new object();
internal long Interval {
get{
double totalNanoseconds;
lock (lockObject) {
totalNanoseconds= (1e9 * pendingNanosleepParams.tv_sec)
+ pendingNanosleepParams.tv_nsec;
}
return (int)(totalNanoseconds * 1e-6);//return value in ms
}
set{
lock (lockObject) {
pendingNanosleepParams.tv_sec = value / 1000;
pendingNanosleepParams.tv_nsec = (long)((value % 1000) * 1e6);//set value in ns
}
}
}
private bool enabled;
internal bool Enabled {
get { return enabled; }
set {
if (value) {
watch.Start();
enabled = value;
Task.Run(()=>tickGenerator()); // fire up new thread
}
else {
lock (lockObject) {
enabled = value;
}
}
}
}
private Task tickGenerator() {
bool bNotPendingStop;
lock (lockObject) {
bNotPendingStop = enabled;
}
while (bNotPendingStop) {
// Check if thread has been told to halt
lock (lockObject) {
bNotPendingStop = enabled;
}
long curTime = watch.ElapsedMilliseconds;
if (curTime >= Interval) {
watch.Restart ();
if (Tick != null)
Tick (this, new EventArgs ());
} else {
long iTimeLeft = (Interval - curTime); // How long to delay for
if (iTimeLeft >= safeDelay) { // Task.Delay has resolution 15ms//await Task.Delay(TimeSpan.FromMilliseconds(iTimeLeft - safeDelay));
threadNanosleepParams.tv_nsec = (int)((iTimeLeft - safeDelay) * 1e6);
threadNanosleepParams.tv_sec = 0;
Syscall.nanosleep (ref threadNanosleepParams, ref threadNanosleepParams);
}
}
}
watch.Stop();
return null;
}
}
Usage:
private myMainFunction(){
LinuxHiResTimer timReallyFast = new LinuxHiResTimer();
timReallyFast.Interval=25; //
timReallyFast.Tick += new EventHandler(timReallyFast_Tick);
timReallyFast.Enabled = true;
}
private void timReallyFast_Tick(System.Object sender, System.EventArgs e) {
// Do this quickly i.e.
PollSerialPort();
}

Related

Beep in C# .NET application on server side

To debug a firewall delay issue I need an application that will produce a beep on server side when it detects an HTTP GET request.
This code (test.ashx):
<%# WebHandler Language="C#" Class="TestHandler" %>
using System;
using System.Web;
public class TestHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
HttpResponse Response = context.Response;
try
{
Response.Write("Before beep");
Console.Beep();
Response.Write("After beep");
}
catch (Exception ex)
{
Response.Write(ex.Message + "<br />\n" + ex.InnerException.Message);
}
}
public bool IsReusable { get { return false; } }
}
produces sound only when debugging in IIS Express. After moving the web app to IIS, the sound disappears.
The three easy ways of producing a sound are System.Console.Beep(), System.Media.SoundPlayer, and System.Media.SystemSounds.Beep().
Unfortunately, these methods only work in desktop applications, and won't work in service applications. When ASP.Net apps are run under IIS Express (a desktop app), these sound methods work. However, when ASP.Net apps are run under the IIS service, the sound methods don't work.
System.Console.Beep() ultimately calls the kernel32.dll Beep() function. It's restricted to desktop apps only (scroll down to the Requirements section).
Same for System.Media.SoundPlayer and System.Media.SystemSounds.Beep(). They call the kernel32.dll MessageBeep() and the winmm.dll PlaySound() functions, respectively. They, too, are restricted to desktop apps.
One way to get sounds to play in a service is use NAudio. It's easy to install via NuGet.
This chunk of code is the only way I could get the sound to play. It has to be played on a separate worker thread, and the execution of the worker thread needs to be paused to let the .wav file finish playing.
using System;
using System.Diagnostics;
using System.Threading;
using NAudio.Dsp;
using NAudio.Wave;
...
protected void Button1_Click(object sender, EventArgs e)
{
var waveFilename = #"c:\Windows\Media\tada.wav";
/* Trying to play the .wav file on the main thread
doesn't seem to work. */
ThreadPool.QueueUserWorkItem(
(state) =>
{
using (var audioPlayback = new AudioPlayback())
{
audioPlayback.Load(waveFilename);
audioPlayback.Play(); // Asynchronous.
/* Need to sleep for the approximate length of .wav file,
otherwise no sound is produced because of the
asynchronous Play() call. */
Thread.Sleep(2000);
}
});
}
Here's the supporting code taken from code in NAudio's NAudioWPFDemo project:
public class MaxSampleEventArgs : EventArgs
{
[DebuggerStepThrough]
public MaxSampleEventArgs(float minValue, float maxValue)
{
this.MaxSample = maxValue;
this.MinSample = minValue;
}
public float MaxSample { get; private set; }
public float MinSample { get; private set; }
}
public class FftEventArgs : EventArgs
{
[DebuggerStepThrough]
public FftEventArgs(Complex[] result)
{
this.Result = result;
}
public Complex[] Result { get; private set; }
}
public class SampleAggregator : ISampleProvider
{
// volume
public event EventHandler<MaxSampleEventArgs> MaximumCalculated;
private float maxValue;
private float minValue;
public int NotificationCount { get; set; }
int count;
// FFT
public event EventHandler<FftEventArgs> FftCalculated;
public bool PerformFFT { get; set; }
private readonly Complex[] fftBuffer;
private readonly FftEventArgs fftArgs;
private int fftPos;
private readonly int fftLength;
private int m;
private readonly ISampleProvider source;
private readonly int channels;
public SampleAggregator(ISampleProvider source, int fftLength = 1024)
{
channels = source.WaveFormat.Channels;
if (!IsPowerOfTwo(fftLength))
throw new ArgumentException("FFT Length must be a power of two");
this.m = (int) Math.Log(fftLength, 2.0);
this.fftLength = fftLength;
this.fftBuffer = new Complex[fftLength];
this.fftArgs = new FftEventArgs(fftBuffer);
this.source = source;
}
private bool IsPowerOfTwo(int x)
{
return (x & (x - 1)) == 0;
}
public void Reset()
{
count = 0;
maxValue = minValue = 0;
}
private void Add(float value)
{
if (PerformFFT && FftCalculated != null)
{
fftBuffer[fftPos].X = (float) (value * FastFourierTransform.HammingWindow(fftPos, fftLength));
fftBuffer[fftPos].Y = 0;
fftPos++;
if (fftPos >= fftBuffer.Length)
{
fftPos = 0;
// 1024 = 2^10
FastFourierTransform.FFT(true, m, fftBuffer);
FftCalculated(this, fftArgs);
}
}
maxValue = Math.Max(maxValue, value);
minValue = Math.Min(minValue, value);
count++;
if (count >= NotificationCount && NotificationCount > 0)
{
if (MaximumCalculated != null)
MaximumCalculated(this, new MaxSampleEventArgs(minValue, maxValue));
Reset();
}
}
public WaveFormat WaveFormat { get { return source.WaveFormat; } }
public int Read(float[] buffer, int offset, int count)
{
var samplesRead = source.Read(buffer, offset, count);
for (int n = 0; n < samplesRead; n += channels)
Add(buffer[n + offset]);
return samplesRead;
}
}
public class AudioPlayback : IDisposable
{
private IWavePlayer _playbackDevice;
private WaveStream _fileStream;
public void Load(string fileName)
{
Stop();
CloseFile();
EnsureDeviceCreated();
OpenFile(fileName);
}
private void CloseFile()
{
if (_fileStream != null)
{
_fileStream.Dispose();
_fileStream = null;
}
}
private void OpenFile(string fileName)
{
try
{
var inputStream = new AudioFileReader(fileName);
_fileStream = inputStream;
var aggregator = new SampleAggregator(inputStream);
aggregator.NotificationCount = inputStream.WaveFormat.SampleRate / 100;
aggregator.PerformFFT = true;
_playbackDevice.Init(aggregator);
}
catch
{
CloseFile();
throw;
}
}
private void EnsureDeviceCreated()
{
if (_playbackDevice == null)
CreateDevice();
}
private void CreateDevice()
{
_playbackDevice = new WaveOut { DesiredLatency = 200 };
}
public void Play()
{
if (_playbackDevice != null && _fileStream != null && _playbackDevice.PlaybackState != PlaybackState.Playing)
_playbackDevice.Play();
}
public void Pause()
{
if (_playbackDevice != null)
_playbackDevice.Pause();
}
public void Stop()
{
if (_playbackDevice != null)
_playbackDevice.Stop();
if (_fileStream != null)
_fileStream.Position = 0;
}
public void Dispose()
{
Stop();
CloseFile();
if (_playbackDevice != null)
_playbackDevice.Dispose();
}
}
Try this System.Media.SystemSounds.Beep.Play();

Run code only once using dispatcher

I have a simple pattern to run code only once. It's mostly used to Update something on the UI, while it may change very often in the Background.
private bool _updating;
private void UpdateSomething()
{
if (!_updating)
{
_updating = true;
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
_updating = false;
DoSomething();
}), DispatcherPriority.Background);
}
}
I would prefer to put the boilerplate code inside a simple method:
public static void RunOnce(Action action, ref bool guard)
{
if (!guard)
{
guard = true;
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
guard = false;
action();
}), DispatcherPriority.Background);
}
}
und call it like this:
void UpdateSomething()
{
RunOnce(DoSomething, ref _updating);
}
However, this does not work as you cannot have ref parameters inside anonymous methods.
Is there any workaround, e.g. to pin the ref parameter and free it when the method was executed?
You could do something like this:
public static void RunOnce(Action action, ref RunOnceToken token)
{
if (token == null || token.IsCompleted)
{
token = new RunOnceToken(
Application.Current.Dispatcher.BeginInvoke(
action,
DispatcherPriority.Background));
}
}
public sealed class RunOnceToken : IDisposable
{
private DispatcherOperation _operation;
public RunOnceToken(DispatcherOperation operation)
{
if (operation != null &&
operation.Status != DispatcherOperationStatus.Completed &&
operation.Status != DispatcherOperationStatus.Aborted)
{
_operation = operation;
_operation.Completed += OnCompletedOrAborted;
_operation.Aborted += OnCompletedOrAborted;
}
}
private void OnCompletedOrAborted(object sender, EventArgs e)
{
this.Dispose();
}
public bool IsCompleted
{
get { return _operation == null; }
}
public void Dispose()
{
var operation = _operation;
if (operation == null)
return;
_operation = null;
operation.Completed -= OnCompletedOrAborted;
operation.Aborted -= OnCompletedOrAborted;
}
}
Your example usage would change to:
private RunOnceToken _updateToken;
private void UpdateSomething()
{
RunOnce(DoSomething, ref _updateToken);
}
It doesn't really matter if you never clear your copy of the token, because the wrapped DispatcherOperation gets cleared out upon completion to avoid leaking action or any values it captures.
In case it wasn't obvious, none of this is concurrency-safe; I assume everything above is only accessed from the UI thread.
One useful enhancement might be to add an optional DispatcherPriority argument to RunOnce such that you can control the priority level used to schedule action (perhaps canceling an already-scheduled operation if it was scheduled at a lower priority).
I was no aware about DispatcherOperation existence, however seen Mike Strobel answer I wrote following code. I'm not 100% sure about it but it seems to work without to much boilerplate.
public static class DispatcherExtensions {
public static int clearInterval = 10_000;
private static long time => DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
private static long lastClearTime = time;
private static Dictionary<int, DispatcherOperation> currOperations = new Dictionary<int, DispatcherOperation>();
private static object sync = new object();
public static void invokeLastAsync(this Dispatcher d, Action a, DispatcherPriority p = DispatcherPriority.Background, [CallerFilePath]object key1 = null, [CallerLineNumber]object key2 = null) {
lock (sync) {
DispatcherOperation dop;
var k = key1.GetHashCode() ^ key2.GetHashCode();
if (currOperations.ContainsKey(k)) {
dop = currOperations[k];
currOperations.Remove(k);
dop.Abort();
}
dop = d.BeginInvoke(a, p);
clearOperations(false);
currOperations.Add(k, dop);
}
}
public static void clearOperations(bool force = true) {
var ct = time;
if (!force && ct - lastClearTime < clearInterval) return;
var nd = new Dictionary<int, DispatcherOperation>();
foreach (var ao in currOperations) {
var s = ao.Value.Status;
if (s == DispatcherOperationStatus.Completed
|| s == DispatcherOperationStatus.Aborted)
nd.Add(ao.Key, ao.Value);
}
currOperations = nd;
lastClearTime = ct;
}
}
Basically extension method take file path and line number as a key to store DispacherOperation instance in a dictionary, and If the key already have an operation, its aborted and replaced with new operation. Periodically, the dictionary is cleared, from completed/aborted actions that are no longer invoked.
The usage is very simple:
private int initCount = 0;
private int invokeCount = 0;
private void updateSomething() {
initCount++;
view.Dispatcher.invokeLastAsync(() => {
Console.WriteLine($#"invoked {++invokeCount}/{initCount}");
});
}
I haven't run to any issue with this so far. Maybe someone else could see some weak spot.

SEHException when calling DeleteTimerQueueTimer

I'm using a Timer class that wraps CreateTimerQueueTimer and DeleteTimerQueueTimer.
Here is the class:
using System;
using System.Threading;
using MyCompany.Internal;
using TimerCallback = MyCompany.Internal.TimerCallback;
public class Timer : IDisposable
{
public Timer()
{
this.callback = this.ticked;
this.autoReset = true;
Computer.ChangeTimerResolutionTo(1);
this.priority = ThreadPriority.Normal;
}
public virtual event EventHandler Elapsed;
public virtual bool AutoReset
{
get
{
return this.autoReset;
}
set
{
this.autoReset = value;
}
}
public virtual ThreadPriority Priority
{
get
{
return this.priority;
}
set
{
this.priority = value;
}
}
public virtual void Start(int interval)
{
if (interval < 1)
{
throw new ArgumentOutOfRangeException("interval", "Interval must be at least 1 millisecond.");
}
if (Interlocked.CompareExchange(ref this.started, 1, 0) == 1)
{
return;
}
NativeMethods.CreateTimerQueueTimer(
out this.handle,
IntPtr.Zero,
this.callback,
IntPtr.Zero,
(uint)interval,
(uint)interval,
CallbackOptions.ExecuteInTimerThread);
}
public virtual void Stop()
{
if (Interlocked.CompareExchange(ref this.started, 0, 1) == 0)
{
return;
}
NativeMethods.DeleteTimerQueueTimer(IntPtr.Zero, this.handle, IntPtr.Zero);
}
public virtual void Dispose()
{
this.Stop();
}
private void ticked(IntPtr parameterPointer, bool unused)
{
if (!this.AutoReset)
{
this.Stop();
}
Thread.CurrentThread.Priority = this.Priority;
var elapsed = this.Elapsed;
if (elapsed != null)
{
elapsed(this, EventArgs.Empty);
}
}
private int started;
private IntPtr handle;
private volatile bool autoReset;
private ThreadPriority priority;
private readonly TimerCallback callback;
}
The problem is, after awhile I'm getting an SEHException when calling Start and Stop simultaneously from multiple threads. The Interlocked.CompareExchange methods should prevent DeleteTimerQueueTimer from being called once after Stop() is called, right? Even if Stop() is called simultaneously from different threads?
The SEHException is being thrown at DeleteTimerQueueTimer(); I assume it's because it's trying to delete a timer that has already been deleted, making the handle invalid. Doesn't the CompareExchange prevent DeleteTimerQueueTimer from being called more than once, even by multiple threads simultaneously?
The function Interlocked.CompareExchange prevent the variable 'started' from modified at same time from 2 threads, but the handle of the timer is the real one you want to protected, but the code fails to do in some cases.
For example, thread A call the start function, it execute the function Interlocked.CompareExchange and then this.started is 1; at this time thread A call the stop function, it sees that 'started' is one, so it will call function DeleteTimerQueueTimer to delete the timer, while the timer might not be created yet and the handle is invalid.
So you should protect the handle of the timer

create time event in c#

I am trying to create an event that executes a function when a certain amount of time has changed. The timer is done by another code, it's supposed to call Plus(1), but will that change all timers (if I create multiple)? And this code is not actually working.
namespace #event
{
class Program
{
static void Main(string[] args)
{
Tick tijd = new Tick();
tijd.interval = 10;
tijd.TijdVeranderd += new EventHandler(Uitvoeren);
dynamic func = new Tick();
for (int i = 0; i < 100; i++)
{
func.Plus(1);
}
Console.ReadLine();
}
static void Uitvoeren(object sender, EventArgs e)
{
Console.WriteLine("Uitgevoerd!");
}
}
public class Tick
{
public event EventHandler TijdVeranderd;
public int interval;
private int tijd;
public void Plus(int i)
{
tijd += 1;
}
public int Verander
{
get { return this.tijd; }
set
{
this.tijd = value;
if (tijd == interval)
{
if (this.TijdVeranderd != null)
this.TijdVeranderd(this, new EventArgs());
tijd = 0;
}
}
}
public Tick() { }
}
}
EDIT: I don't want to use the .net timer, I want to create my own.
Just use .net timer like this:
System.Timers.Timer aTimer = new System.Timers.Timer();
aTimer.Elapsed += new System.Timers.ElapsedEventHandler(aTimer_Elapsed);
aTimer.Interval = 1000; //here you can set your interval
aTimer.Start();
Here you can catch the event and call other method:
void aTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
//TODO: call your method like "Plus"
}
There seem to be a couple of errors in your code:
First of all, in Main, you are calling Plus() on a different instance of Tick than the one that you configured. Try using tijd.Plus(1) instead of func.Plus(1).
Also, in the implementation of Plus, when you increment the private variable tijd, the code associated with the property Verander does not get executed, so no events ever fire. To quickly fix, increment Verander instead of tijd.
namespace #event
{
class Program
{
static void Main(string[] args)
{
Tick tijd = new Tick();
tijd.interval = 10;
tijd.TijdVeranderd += new EventHandler(Uitvoeren);
for (int i = 0; i < 100; i++)
{
tijd.Plus(1);
}
Console.ReadLine();
}
static void Uitvoeren(object sender, EventArgs e)
{
Console.WriteLine("Uitgevoerd!");
}
}
public class Tick
{
public event EventHandler TijdVeranderd;
public int interval;
private int tijd;
public void Plus(int i)
{
Verander += 1;
}
public int Verander
{
get { return this.tijd; }
set
{
this.tijd = value;
if (tijd == interval)
{
if (this.TijdVeranderd != null)
this.TijdVeranderd(this, new EventArgs());
tijd = 0;
}
}
}
public Tick() { }
}
}
You can try with this code
private static System.Timers.Timer aTimer;
public static void Main()
{
// Create a timer with a ten second interval.
aTimer = new System.Timers.Timer(10000);
// Hook up the Elapsed event for the timer.
aTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
// Set the Interval to 2 seconds (2000 milliseconds).
aTimer.Interval = 2000;
aTimer.Enabled = true;
Console.WriteLine("Press the Enter key to exit the program.");
Console.ReadLine();
// If the timer is declared in a long-running method, use
// KeepAlive to prevent garbage collection from occurring
// before the method ends.
//GC.KeepAlive(aTimer);
}
// Specify what you want to happen when the Elapsed event is
// raised.
private static void OnTimedEvent(object source, ElapsedEventArgs e)
{
Console.WriteLine("The Elapsed event was raised at {0}", e.SignalTime);
}
I don't know why you wouldn't use a system timer, but here's an [untested] implementation of a timer that should do the trick:
class MyCrudeTimer : IDisposable
{
public event EventHandler Alarm ;
public TimeSpan Duration { get ; private set ; }
public bool AutomaticallyReset { get ; private set ; }
public bool IsRunning { get ; private set ; }
private Thread timerThread ;
private ManualResetEvent start ;
private void TimerCore()
{
try
{
while ( start.WaitOne() )
{
System.Threading.Thread.Sleep( Duration ) ;
Alarm( this , new EventArgs() ) ;
}
}
catch ( ThreadAbortException )
{
}
catch ( ThreadInterruptedException )
{
}
return ;
}
public MyCrudeTimer( TimeSpan duration , bool autoReset )
{
if ( duration <= TimeSpan.Zero ) throw new ArgumentOutOfRangeException("duration must be positive","duration") ;
this.Duration = duration ;
this.AutomaticallyReset = autoReset ;
this.start = new ManualResetEvent(false) ;
this.timerThread = new Thread( TimerCore ) ;
this.timerThread.Start() ;
return ;
}
public void Start()
{
if ( IsRunning ) throw new InvalidOperationException() ;
IsRunning = true ;
start.Set() ;
return ;
}
public void Stop()
{
if ( !IsRunning ) throw new InvalidOperationException() ;
IsRunning = false ;
start.Reset() ;
return ;
}
public void Dispose()
{
try
{
if ( this.timerThread != null )
{
this.timerThread.Abort() ;
this.timerThread = null ;
}
}
catch
{
}
return ;
}
}

Frequent lag spikes in multi-threaded code

I'm attempting to make my simple C# graphics library multi-threaded. However, after the introduction of this code:
/* foreach (IAffector affector in affectorLookup.Values)
affector.Update(timestep); */
taskManager.Value = timestep; taskManager.Start();
foreach (IAffector affector in affectorLookup.Values)
taskManager.AddToQueue(affector.Update);
taskManager.StopWhenDone();
taskManager.Wait();
the simulation starts experiencing sharp lag-spikes, which seem to originate in TaskHandler.Run (I can't tell for sure, because adding the previous code makes my code profiler ignore anything outside TaskHandler.Run).
The task manager:
public class TaskManager
{
public delegate void MethodDel(float timestep);
private Queue<MethodDel> queue;
private List<TaskHandler> handlers;
private float value;
public float Value
{
get
{
return value;
}
set
{
this.value = value;
}
}
public TaskManager()
{
this.queue = new Queue<MethodDel>();
this.handlers = new List<TaskHandler>(System.Environment.ProcessorCount);
for (int t = 0; t < this.handlers.Capacity; ++t)
this.handlers.Add(new TaskHandler(this));
this.value = 0;
}
public void Start()
{
foreach (var handler in handlers)
handler.Start();
}
public void Stop()
{
lock (queue)
queue.Clear();
foreach (var handler in handlers)
handler.StopWhenDone();
}
public void StopWhenDone()
{
foreach (var handler in handlers)
handler.StopWhenDone();
}
public void AddToQueue(MethodDel method)
{
lock (queue)
queue.Enqueue(method);
}
public bool GetFromQueue(out MethodDel method)
{
lock (queue)
{
if (queue.Count == 0) { method = null; return false; }
method = queue.Dequeue();
return true;
}
}
public int GetQueueCount()
{
return queue.Count;
}
internal void Wait()
{
// Have to wait for them one at a time because the main thread is STA.
WaitHandle[] waitHandles = new WaitHandle[1];
// for (int t = 0; t < handlers.Count; ++t)
// waitHandles[t] = handlers[t].WaitHandle;
// WaitHandle.WaitAll(waitHandles);
for (int t = 0; t < handlers.Count; ++t)
{ waitHandles[0] = handlers[t].WaitHandle; WaitHandle.WaitAll(waitHandles); }
}
}
And the task handler:
public class TaskHandler
{
private TaskManager manager;
private Thread thread;
private bool stopWhenDone;
private ManualResetEvent waitHandle;
public ManualResetEvent WaitHandle
{
get
{
return waitHandle;
}
}
public TaskHandler(TaskManager manager)
{
this.manager = manager;
}
public void Start()
{
waitHandle = new ManualResetEvent(false);
stopWhenDone = false;
thread = new Thread(Run);
thread.IsBackground = true;
thread.SetApartmentState(ApartmentState.MTA);
thread.Start();
}
public void StopWhenDone()
{
this.stopWhenDone = true;
}
// Possible source of slowdown
private void Run()
{
TaskManager.MethodDel curMethod;
while (!stopWhenDone || manager.GetQueueCount() > 0)
{
if (manager.GetFromQueue(out curMethod))
{
curMethod(manager.Value);
}
}
waitHandle.Set();
}
}
Starting a thread is a heavy operation. Not sure if it's as heavy as you are experiencing, but that could be it. Also, having all your processing run parallel can be putting a big strain on your system with possibly little benefit...
I'm going to venture that the spikes have something to do with waitHandle.Set();
I like the overall design, but I have not used WaitHandle before, so I am unsure how this interacts with your design.

Categories