How to stop System.Threading.Timer in callback method - c#

How can I stop System.Threading.Timer in it's call back method. I referenced MSDN, but couldn't find anything useful. Please help.

First, the callback method must have the timer instance in-scope.
Then the simple incantation
timerInstance.Change( Timeout.Infinite , Timeout.Infinite ) ;
will shut down the timer. It is possible that the timer might invoke the callback method once more after the change, I believe, depending on the state it's in.

timer.Change(Timeout.Infinite, Timeout.Infinite);

Try this:
If you want you could let timer continue firing the callback method and include the code below
private void CreatorLoop(object state)
{
if (Monitor.TryEnter(lockObject)
{
try
{
// Work here
}
finally
{
Monitor.Exit(lockObject);
}
}
}
check out this link too:
Stopping timer in its callback method

You can simply call myTimer.Change(Timeout.Infinite, Timeout.Infinite).
Technically, only the first parameter (dueTime) needs to be specified as Timeout.Infinite for the timer to stop.
For more information, see Timer.Change Method.

I found out the hard way that Change(Timeout.Infinite, Timeout.Infinite) isn't quite reliable, and switched over to System.Timers.Timer with AutoReset = false.

The problem with Timer is that it might be called after disposing its owner class. The following implementation worked for me by using the state object of the Timer initializer. Heap will not remove that object until it is consumed. This was my only way to gracefully cleanup timer callback.
using System;
using System.Threading;
namespace TimerDispose
{
/// <summary>
/// A timer-containing class that can be disposed safely by allowing the timer
/// callback that it must exit/cancel its processes
/// </summary>
class TimerOwner : IDisposable
{
const int dueTime = 5 * 100; //halve a second
const int timerPeriod = 1 * 1000; //Repeat timer every one second (make it Timeout.Inifinite if no repeating required)
private TimerCanceller timerCanceller = new TimerCanceller();
private Timer timer;
public TimerOwner()
{
timerInit(dueTime);
}
byte[] dummy = new byte[100000];
/// <summary>
///
/// </summary>
/// <param name="dueTime">Pass dueTime for the first time, then TimerPeriod will be passed automatically</param>
private void timerInit(int dueTime)
{
timer = new Timer(timerCallback,
timerCanceller, //this is the trick, it will be kept in the heap until it is consumed by the callback
dueTime,
Timeout.Infinite
);
}
private void timerCallback(object state)
{
try
{
//First exit if the timer was stoped before calling callback. This info is saved in state
var canceller = (TimerCanceller)state;
if (canceller.Cancelled)
{
return; //
}
//Your logic goes here. Please take care ! the callback might have already been called before stoping the timer
//and we might be already here after intending of stoping the timer. In most cases it is fine but try not to consume
//an object of this class because it might be already disposed. If you have to do that, hopefully it will be catched by
//the ObjectDisposedException below
dummy[1] = 50; //just messing up with the object after it might be disposed/nulled
//Yes, we need to check again. Read above note
if (canceller.Cancelled)
{
//Dispose any resource that might have been initialized above
return; //
}
if (timerPeriod != Timeout.Infinite)
{
timerInit(timerPeriod);
}
}
catch (ObjectDisposedException ex)
{
Console.WriteLine("A disposed object accessed");
}
catch (NullReferenceException ex)
{
Console.WriteLine("A nulled object accessed");
}
catch (Exception ex)
{
}
}
public void releaseTimer()
{
timerCanceller.Cancelled = true;
timer.Change(Timeout.Infinite, Timeout.Infinite);
timer.Dispose();
}
public void Dispose()
{
releaseTimer();
dummy = null; //for testing
GC.SuppressFinalize(this);
}
}
class TimerCanceller
{
public bool Cancelled = false;
}
/// <summary>
/// Testing the implementation
/// </summary>
class Program
{
static void Main(string[] args)
{
var list = new System.Collections.Generic.List<TimerOwner>();
Console.WriteLine("Started initializing");
for (int i = 0; i < 500000; i++)
{
list.Add(new TimerOwner());
}
Console.WriteLine("Started releasing");
foreach (var item in list)
{
item.Dispose();
}
Console.WriteLine("Press any key to exit");
Console.ReadKey();
}
}
}

Related

Keeping timer from Garbage collected in a console application

All, I am monitoring my outlook inbox for some emails and parses that based on the content. I do this by running a console application and triggering a timer as shown below. The problem is this gets garbage collected after some time and I have to restart the app manually. I cannot run this inside a windows service as I get some permission issues while calling the Outlook api. Please see my code below
I tried doing a GC.SuppressFinalize(), GC.KeepAlive() on the timer object but no avail.
class Program
{
private static System.Timers.Timer _timer = new System.Timers.Timer();
static void Main(string[] args)
{
_timer.Interval = 10000;
_timer.Start();
_timer.Elapsed += Timer_Elapsed1;
Console.ReadLine();
}
}
private static void Timer_Elapsed1(object sender, System.Timers.ElapsedEventArgs e)
{
try
{
_timer.Stop();
AddNumbers(2, 2);
Console.WriteLine("The current time now is :{0}", DateTime.Now.ToString());
GC.SuppressFinalize(_timer);
_timer.Start();
}
catch (Exception ex)
{
_timer.Start();
Console.WriteLine("Timer restarted from exception");
}
finally
{
_timer.Start();
}
}
private static void AddNumbers(int x, int y)
{
var sum = x + y;
Console.WriteLine(sum);
}
In the scope of your example application your Timer is not being garbage collected so you must be getting another error. Also, please see my note and example below about GC.SuppressFinalize because what you're implying and what it does are two different things.
Loop instead of Timer
Just a suggestion: When using a Timer and the Elapsed event calls the timer to stop and then start again once complete this is a sign you need to have a simple loop running instead with a thread wait.
Since this is a console app and there are no other threads required I'll post a simple looping example that, IMO, would be more efficient and easier to manage.
using System;
using System.Threading;
namespace Question_Answer_Console_App
{
class Program
{
private const int SleepTimeMS = 10000;
static void Main(string[] args)
{
ThreadPool.QueueUserWorkItem(new WaitCallback((state) =>
{
while (true)
{
Thread.Sleep(SleepTimeMS);
AddNumbers(2, 2);
Console.WriteLine("The current time now is :{0}", DateTime.Now.ToString());
}
}));
Console.Read();
}
private static void AddNumbers(int x, int y)
{
var sum = x + y;
Console.WriteLine(sum);
}
}
}
GC.SuppressFinalize Information
Also, GC.SuppressFinalize does not have anything to do with garbage collection; it just tells the garbage collector, that when collected, not to run the finalizer on the object (the destructor method in C#). This is useful when there are items already disposed and you don't want to reproduce the work... For example if you're object is IDisposable, and you place the Dispose method in the destructor, and the user properly disposes of it when it is being used then you may want to skip the destructor. Here's an example of using GC.SuppressFinalize properly.
public class SomethingDisposable : IDisposable
{
public void Dispose()
{
//Dispose of some unmanaged resources or something.
GC.SuppressFinalize(this); //We can safely skip the Destructor method (Finalizer)
}
~SomethingDisposable() => Dispose(); //Just incase the object is garbage collected and never properly disposed.
}
GC.SuppressFinalize Example
And just for show here's a console app that illustrates the GC.SuppressFinalize in work. I make 2 IDisposable objects. The first I actually dispose of properly with a using statement and the second I don't. Once they are out of scope they are up for garbage collection. I intentionally call the garbage collector and wait for all finalizers to be called so that we can immediately see results.
Notice the disposed object DOES NOT call the finalizer and the object that was not disposed does (which in turn calls dispose... which is the proper way to implement it.)
using System;
namespace Question_Answer_Console_App
{
class Program
{
private const string DisposableTestId = "Disposable-Test";
private const string FinalizingTestId = "Finalizing-Test";
static void Main(string[] args)
{
TestDisposing();
Console.WriteLine();
TestFinalizing();
GC.Collect();
GC.WaitForPendingFinalizers();
Console.Read();
}
private static void TestDisposing()
{
using (var disposingTest = new TestSuppressFinalize(DisposableTestId))
PrintTesting(disposingTest);
}
private static void TestFinalizing()
{
var finalizingTest = new TestSuppressFinalize(FinalizingTestId);
PrintTesting(finalizingTest);
}
private static void PrintTesting(TestSuppressFinalize finalizingTest)
=> Console.WriteLine($"Testing {finalizingTest.TestId.ToString()}");
}
public class TestSuppressFinalize : IDisposable
{
public TestSuppressFinalize(string testId) => TestId = testId;
public string TestId { get; }
public void Dispose()
{
Console.WriteLine($"Disposed {TestId.ToString()}");
GC.SuppressFinalize(this);
}
~TestSuppressFinalize()
{
Console.WriteLine($"Finalized {TestId.ToString()}");
Dispose();
}
}
}
Output:
Testing Disposable-Test
Disposed Disposable-Test
Testing Finalizing-Test
Finalized Finalizing-Test
Disposed Finalizing-Test

How to get back to function from timed event

Okay so I have a function called readSensor which you guessed it.. reads a sensor.
But the sensors usually take about 100ms to respond. So in the readSensor function I am basically just starting a timer.
On the timed event I read the serialport and get my response.
However this means that my response is in the onTimedEvent when I want it to be in the readSensor function..
Basically from the main form I want to be able to do this.
value = readSensor()
when at the minute all I can do is readSensor() and then I can see the response is coming back by displaying it in a messagebox once the timedEvent fires.
here is my code. (I have missed out loads of serialport setup and stuff but hopefully you can see the problem I am in)
I don't want to just wait in the function for 100ms though polling the timer as that will make my program slow..
I want to somehow get the response back to the readSensor function and then back to the form.
using System;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO.Ports;
using System.Timers;
namespace readSensor
{
public partial class readSens : UserControl
{
public readSens()
{
InitializeComponent();
}
private System.Timers.Timer rTimer;
SerialPort sp = new SerialPort();
private void setupTimer()
{
// Create a timer with a 100ms response.
rTimer = new System.Timers.Timer(100);
rTimer.SynchronizingObject = this;
// Hook up the Elapsed event for the timer.
rTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
}
private void OnTimedEvent(object source, ElapsedEventArgs e)
{
string response = getResponse();
}
public string getResponse()
{
string status = "";
byte[] readBuffer = new byte[255];
if (sp.IsOpen)
{
if (sp.BytesToRead > 0) //there is data to read
{
int length = sp.BytesToRead;
for (int i = 0; i < length; i++)
{
readBuffer[i] = (byte)sp.ReadByte();
status = "pass";
return status;
}
}
}
public void readSensor(byte addr)
{
if (!sp.IsOpen)
{
openPort();
readSensor(addr); // calls itself again once port is opened
}
else if (sp.IsOpen)
{
rTimer.Start();
}
else
{
MessageBox.Show("Port not opened yet");
}
}
}
}
In the main form I am basically just saying
setupTimer();
readSensor();
on a button click.
I don't think you can do it without some callback mechanism. You could implement a while loop but that is not eficient as it would introduce spinning.
My advice is to implement a proper async pattern or something simple like:
ReadSensor(addr, DoSomethingWithResult);
public void DoSomethingWithResult(string result)
{
Console.WriteLine (result);
}
public partial class ReadSens : UserControl
{
private Action<string> _responseCallback;
public void ReadSensor(byte addr, Action<string> responseCallback)
{
_responseCallback = responseCallback;
// initiate timer
}
private void OnTimedEvent(object source, ElapsedEventArgs e)
{
string response = getResponse();
_responseCallback(response);
}
}
Start a separate thread, then from that thread write into a queue the results back in your main thread.
class Game1
{
//We declare a queue, which is like an array that we can extract and enter data easily in a FIFO (first in, first out) style list.
Queue<string> q = new Queue<string>();
public void threadStart(object obj)
{
//We get the result of your function, while our main function is still looping and waiting.
string result = readInput()
//We tell C# that the parameter we passed in, is in fact the Game1 class passed from "t.Start"
Game1 game = (Game1)obj;
//This puts our "result" into the queue.
game.q.Enqueue(result);
}
public void start()
{
//Declares a new thread, which will run "threadStart" function.
System.Threading.Thread t = new System.Threading.Thread(threadStart);
//We start the other thread (that will run in parallel) and pass "this" as the parameter.
t.Start(this);
//We loop over and over, sleeping, whilst the other function runs at the same time. This is called "multi- threading"
while (q.Count == 0)
{
System.Threading.Thread.Sleep(10);
}
//This gets the last-entered (oldest) value from the queue q.
string result = q.Deque();
}
}
So this sets off a thread to get the result, and then in my version, polls the queue for a while until the results come back, but in yours could do a bunch of stuff, as long as you check the queue every now and again for new data.
Edit: Added commenting to hopefully alleviate some of your questions.
Could be this approach a valid solution for you? I think you only are using Timer to wait the serialPort to be open, but it can be self-controlled with raising event.
public class SensorReader
{
private Sensor sensor;
private string lastResponse;
public SensorReader(SerialPort serialPort)
{
this.serialPort = aSerialPort.
this.sensor = new Sensor(serialPort);
this.sensor.PortOpen += PortOpenEventHandler(OnPortOpen);
}
private void OnPortOpen()
{
this.ReadPort();
}
public string ReadPort(byte address)
{
if (!this.sensor.IsOpen)
{
this.sensor.OpenPort();
this.lastResponse = "The serial port doesn't respond... yet!";
}
else
{
// Read response at this point.
this.lastResponse = this.GetResponse();
}
return this.lastResponse;
}
}
public class Sensor
{
private SerialPort serialPort;
public Sensor(SerialPort aSerialPort)
{
this.serialPort = aSerialPort;
}
public bool IsOpen
{
get { return this.serialPort.IsOpen; }
}
public delegate void PortOpenEventHandler(object sender, EventArgs e);
public event PortOpenEventHandler PortOpen;
public void OpenPort()
{
// Open port here...
// ... and throw the PortOpen event.
if (this.PortOpen != null)
{
this.PortOpen(this, EventArgs.Empty);
}
}
}

Non-reentrant timers

I have a function that I want to invoke every x seconds, but I want it to be thread-safe.
Can I set up this behavior when I am creating the timer? (I don't mind which .NET timer I use, I just want it to be thread-safe).
I know I can implement locks inside my callback function, but I think it would be more elegant if it were in the timer level.
My callback function, and environment are not related to a UI.
[Edit 1]
I just don't want there to be more than one thread inside my callback function.
[Edit 2]
I want to keep the locking inside the timer level, because the timer is responsible for when to call my callback, and here there is a particular situation when I don't want to call my callback function. So I think when to call is the responsibility of the timer.
I'm guessing, as your question is not entirely clear, that you want to ensure that your timer cannot re-enter your callback whilst you are processing a callback, and you want to do this without locking. You can achieve this using a System.Timers.Timer and ensuring that the AutoReset property is set to false. This will ensure that you have to trigger the timer on each interval manually, thus preventing any reentrancy:
public class NoLockTimer : IDisposable
{
private readonly Timer _timer;
public NoLockTimer()
{
_timer = new Timer { AutoReset = false, Interval = 1000 };
_timer.Elapsed += delegate
{
//Do some stuff
_timer.Start(); // <- Manual restart.
};
_timer.Start();
}
public void Dispose()
{
if (_timer != null)
{
_timer.Dispose();
}
}
}
Complementing Tim Lloyd's solution for System.Timers.Timer, here's a solution to prevent reentrancy for cases where you want to use System.Threading.Timer instead.
TimeSpan DISABLED_TIME_SPAN = TimeSpan.FromMilliseconds(-1);
TimeSpan interval = TimeSpan.FromSeconds(1);
Timer timer = null; // assign null so we can access it inside the lambda
timer = new Timer(callback: state =>
{
doSomeWork();
try
{
timer.Change(interval, DISABLED_TIME_SPAN);
}
catch (ObjectDisposedException timerHasBeenDisposed)
{
}
}, state: null, dueTime: interval, period: DISABLED_TIME_SPAN);
I believe you don't want interval to be accessed inside of the callback, but that is be easy to fix, if you want to: Put the above into a NonReentrantTimer class that wraps the BCL's Timer class. You would then pass the doSomeWork callback in as a parameter. An example of such a class:
public class NonReentrantTimer : IDisposable
{
private readonly TimerCallback _callback;
private readonly TimeSpan _period;
private readonly Timer _timer;
public NonReentrantTimer(TimerCallback callback, object state, TimeSpan dueTime, TimeSpan period)
{
_callback = callback;
_period = period;
_timer = new Timer(Callback, state, dueTime, DISABLED_TIME_SPAN);
}
private void Callback(object state)
{
_callback(state);
try
{
_timer.Change(_period, DISABLED_TIME_SPAN);
}
catch (ObjectDisposedException timerHasBeenDisposed)
{
}
}
public void Dispose()
{
_timer.Dispose();
}
}
I know I can implement locks inside my callback function, but I think it will be more elegant if it will be in the timer level
If locking is necessary then how could a timer arrange that? You're looking for a magical freebie.
Re Edit1:
Your choices are System.Timers.Timer and System.Threading.Timer, both need precautions against re-entrance. See this page and look for the Dealing with Timer Event Reentrance section.
using System;
using System.Diagnostics;
/// <summary>
/// Updated the code.
/// </summary>
public class NicerFormTimer : IDisposable {
public void Dispose() {
using ( this.Timer ) { }
GC.SuppressFinalize( this );
}
private System.Windows.Forms.Timer Timer { get; }
/// <summary>
/// Perform an <paramref name="action" /> after the given interval (in <paramref name="milliseconds" />).
/// </summary>
/// <param name="action"></param>
/// <param name="repeat">Perform the <paramref name="action" /> again. (Restarts the <see cref="Timer" />.)</param>
/// <param name="milliseconds"></param>
public NicerFormTimer( Action action, Boolean repeat, Int32? milliseconds = null ) {
if ( action == null ) {
return;
}
this.Timer = new System.Windows.Forms.Timer {
Interval = milliseconds.GetValueOrDefault( 1000 )
};
this.Timer.Tick += ( sender, args ) => {
try {
this.Timer.Stop();
action();
}
catch ( Exception exception ) {
Debug.WriteLine( exception );
}
finally {
if ( repeat ) {
this.Timer.Start();
}
}
};
this.Timer.Start();
}
}
/// <summary>
/// Updated the code.
/// </summary>
public class NicerSystemTimer : IDisposable {
public void Dispose() {
using ( this.Timer ) { }
GC.SuppressFinalize( this );
}
private System.Timers.Timer Timer { get; }
/// <summary>
/// Perform an <paramref name="action" /> after the given interval (in <paramref name="milliseconds" />).
/// </summary>
/// <param name="action"></param>
/// <param name="repeat">Perform the <paramref name="action" /> again. (Restarts the <see cref="Timer" />.)</param>
/// <param name="milliseconds"></param>
public NicerSystemTimer( Action action, Boolean repeat, Double? milliseconds = null ) {
if ( action == null ) {
return;
}
this.Timer = new System.Timers.Timer {
AutoReset = false,
Interval = milliseconds.GetValueOrDefault( 1000 )
};
this.Timer.Elapsed += ( sender, args ) => {
try {
this.Timer.Stop();
action();
}
catch ( Exception exception ) {
Debug.WriteLine( exception );
}
finally {
if ( repeat ) {
this.Timer.Start();
}
}
};
this.Timer.Start();
}
}
How timer could know about your shared data?
Timer callback is executed on some ThreadPool thread. So you will have at least 2 threads:
Your main thread where timer is created and launched;
Thread from ThreadPool for launching callback.
And it is your responsibility to provide correct work with your shared data.
Re edits: chibacity provided the perfect example.

How do I wait for a C# event to be raised?

I have a Sender class that sends a Message on a IChannel:
public class MessageEventArgs : EventArgs {
public Message Message { get; private set; }
public MessageEventArgs(Message m) { Message = m; }
}
public interface IChannel {
public event EventHandler<MessageEventArgs> MessageReceived;
void Send(Message m);
}
public class Sender {
public const int MaxWaitInMs = 5000;
private IChannel _c = ...;
public Message Send(Message m) {
_c.Send(m);
// wait for MaxWaitInMs to get an event from _c.MessageReceived
// return the message or null if no message was received in response
}
}
When we send messages, the IChannel sometimes gives a response depending on what kind of Message was sent by raising the MessageReceived event. The event arguments contain the message of interest.
I want Sender.Send() method to wait for a short time to see if this event is raised. If so, I'll return its MessageEventArgs.Message property. If not, I return a null Message.
How can I wait in this way? I'd prefer not to have do the threading legwork with ManualResetEvents and such, so sticking to regular events would be optimal for me.
Use a AutoResetEvent.
Gimme a few minutes and I'll throw together a sample.
Here it is:
public class Sender
{
public static readonly TimeSpan MaxWait = TimeSpan.FromMilliseconds(5000);
private IChannel _c;
private AutoResetEvent _messageReceived;
public Sender()
{
// initialize _c
this._messageReceived = new AutoResetEvent(false);
this._c.MessageReceived += this.MessageReceived;
}
public Message Send(Message m)
{
this._c.Send(m);
// wait for MaxWaitInMs to get an event from _c.MessageReceived
// return the message or null if no message was received in response
// This will wait for up to 5000 ms, then throw an exception.
this._messageReceived.WaitOne(MaxWait);
return null;
}
public void MessageReceived(object sender, MessageEventArgs e)
{
//Do whatever you need to do with the message
this._messageReceived.Set();
}
}
Have you tried assigning the function to call asynchronously to a delegate, then invoking the mydelegateinstance.BeginInvoke?
Linky for reference.
With the below example, just call
FillDataSet(ref table, ref dataset);
and it'll work as if by magic. :)
#region DataSet manipulation
///<summary>Fills a the distance table of a dataset</summary>
private void FillDataSet(ref DistanceDataTableAdapter taD, ref MyDataSet ds) {
using (var myMRE = new ManualResetEventSlim(false)) {
ds.EnforceConstraints = false;
ds.Distance.BeginLoadData();
Func<DistanceDataTable, int> distanceFill = taD.Fill;
distanceFill.BeginInvoke(ds.Distance, FillCallback<DistanceDataTable>, new object[] { distanceFill, myMRE });
WaitHandle.WaitAll(new []{ myMRE.WaitHandle });
ds.Distance.EndLoadData();
ds.EnforceConstraints = true;
}
}
/// <summary>
/// Callback used when filling a table asynchronously.
/// </summary>
/// <param name="result">Represents the status of the asynchronous operation.</param>
private void FillCallback<MyDataTable>(IAsyncResult result) where MyDataTable: DataTable {
var state = result.AsyncState as object[];
Debug.Assert((state != null) && (state.Length == 2), "State variable is either null or an invalid number of parameters were passed.");
var fillFunc = state[0] as Func<MyDataTable, int>;
var mre = state[1] as ManualResetEventSlim;
Debug.Assert((mre != null) && (fillFunc != null));
int rowsAffected = fillFunc.EndInvoke(result);
Debug.WriteLine(" Rows: " + rowsAffected.ToString());
mre.Set();
}
Perhaps your MessageReceived method should simply flag a value to a property of your IChannel interface, while implementing the INotifyPropertyChanged event handler, so that you would be advised when the property is changed.
By doing so, your Sender class could loop until the max waiting time is elapsed, or whenever the PropertyChanged event handler occurs, breaking the loop succesfully. If your loop doesn't get broken, then the message shall be considered as never received.
Useful sample with AutoResetEvent:
using System;
using System.Threading;
class WaitOne
{
static AutoResetEvent autoEvent = new AutoResetEvent(false);
static void Main()
{
Console.WriteLine("Main starting.");
ThreadPool.QueueUserWorkItem(
new WaitCallback(WorkMethod), autoEvent);
// Wait for work method to signal.
autoEvent.WaitOne();
Console.WriteLine("Work method signaled.\nMain ending.");
}
static void WorkMethod(object stateInfo)
{
Console.WriteLine("Work starting.");
// Simulate time spent working.
Thread.Sleep(new Random().Next(100, 2000));
// Signal that work is finished.
Console.WriteLine("Work ending.");
((AutoResetEvent)stateInfo).Set();
}
}
WaitOne is really the right tool for this job. In short, you want to wait between 0 and MaxWaitInMs milliseconds for a job to complete. You really have two choices, poll for completion or synchronize the threads with some construct that can wait an arbitrary amount of time.
Since you're well aware of the right way to do this, for posterity I'll post the polling version:
MessageEventArgs msgArgs = null;
var callback = (object o, MessageEventArgs args) => {
msgArgs = args;
};
_c.MessageReceived += callback;
_c.Send(m);
int msLeft = MaxWaitInMs;
while (msgArgs == null || msLeft >= 0) {
Thread.Sleep(100);
msLeft -= 100; // you should measure this instead with say, Stopwatch
}
_c.MessageRecieved -= callback;

What is wrong with my custom thread pool?

I've created a custom thread pool utility, but there seems to be a problem that I cannot find.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading;
namespace iWallpaper.S3Uploader
{
public class QueueManager<T>
{
private readonly Queue queue = Queue.Synchronized(new Queue());
private readonly AutoResetEvent res = new AutoResetEvent(true);
private readonly AutoResetEvent res_thr = new AutoResetEvent(true);
private readonly Semaphore sem = new Semaphore(1, 4);
private readonly Thread thread;
private Action<T> DoWork;
private int Num_Of_Threads;
private QueueManager()
{
Num_Of_Threads = 0;
maxThread = 5;
thread = new Thread(Worker) {Name = "S3Uploader EventRegisterer"};
thread.Start();
// log.Info(String.Format("{0} [QUEUE] FileUploadQueueManager created", DateTime.Now.ToLongTimeString()));
}
public int maxThread { get; set; }
public static FileUploadQueueManager<T> Instance
{
get { return Nested.instance; }
}
/// <summary>
/// Executes multythreaded operation under items
/// </summary>
/// <param name="list">List of items to proceed</param>
/// <param name="action">Action under item</param>
/// <param name="MaxThreads">Maximum threads</param>
public void Execute(IEnumerable<T> list, Action<T> action, int MaxThreads)
{
maxThread = MaxThreads;
DoWork = action;
foreach (T item in list)
{
Add(item);
}
}
public void ExecuteNoThread(IEnumerable<T> list, Action<T> action)
{
ExecuteNoThread(list, action, 0);
}
public void ExecuteNoThread(IEnumerable<T> list, Action<T> action, int MaxThreads)
{
foreach (T wallpaper in list)
{
action(wallpaper);
}
}
/// <summary>
/// Default 10 threads
/// </summary>
/// <param name="list"></param>
/// <param name="action"></param>
public void Execute(IEnumerable<T> list, Action<T> action)
{
Execute(list, action, 10);
}
private void Add(T item)
{
lock (queue)
{
queue.Enqueue(item);
}
res.Set();
}
private void Worker()
{
while (true)
{
if (queue.Count == 0)
{
res.WaitOne();
}
if (Num_Of_Threads < maxThread)
{
var t = new Thread(Proceed);
t.Start();
}
else
{
res_thr.WaitOne();
}
}
}
private void Proceed()
{
Interlocked.Increment(ref Num_Of_Threads);
if (queue.Count > 0)
{
var item = (T) queue.Dequeue();
sem.WaitOne();
ProceedItem(item);
sem.Release();
}
res_thr.Set();
Interlocked.Decrement(ref Num_Of_Threads);
}
private void ProceedItem(T activity)
{
if (DoWork != null)
DoWork(activity);
lock (Instance)
{
Console.Title = string.Format("ThrId:{0}/{4}, {1}, Activity({2} left):{3}",
thread.ManagedThreadId, DateTime.Now, queue.Count, activity,
Num_Of_Threads);
}
}
#region Nested type: Nested
protected class Nested
{
// Explicit static constructor to tell C# compiler
// not to mark type as beforefieldinit
internal static readonly QueueManager<T> instance = new FileUploadQueueManager<T>();
}
#endregion
}
}
Problem is here:
Console.Title = string.Format("ThrId:{0}/{4}, {1}, Activity({2} left):{3}",
thread.ManagedThreadId, DateTime.Now, queue.Count, activity,
Num_Of_Threads);
There is always ONE thread id in title. And program seems to be working in one thread.
Sample usage:
var i_list = new int[] {1, 2, 4, 5, 6, 7, 8, 6};
QueueManager<int>.Instance.Execute(i_list,
i =>
{
Console.WriteLine("Some action under element number {0}", i);
}, 5);
P.S.: it's pretty messy, but I'm still working on it.
I looked through your code and here are a couple of issues I saw.
You lock the queue object even though it is synchronized queue. This is unnecessary
You inconsistently lock the queue object. It should either be locked for every access or not locked and depending on the Synchronized behavior.
The Proceed method is not thread safe. These two lines are the issue
if (queue.Count > 0) {
var item = (T)queue.Dequeue();
...
}
Using a synchronized queue only guarantees that individual accesses are safe. So both the .Count and the .Dequeue method won't mess with te internal structure of the queue. However imagine the scenario where two threads run these lines of code at the same time with a queue of count 1
Thread1: if (...) -> true
Thread2: if (...) -> true
Thread1: dequeue -> sucess
Thread2: dequeue -> fails because the queue is empty
There is a race condition between Worker and Proceed that can lead to deadlock. The following two lines of code should be switched.
Code:
res_thr.Set()
Interlocked.Decrement(ref Num_Of_Threads);
The first line will unblock the Worker method. If it runs quickly enough it will go back through the look, notice that Num_Of_Threads < maxThreads and go right back into res_thr.WaitOne(). If no other threads are currently running then this will lead to a deadlock in your code. This is very easy to hit with a low number of maximum threads (say 1). Inverting these two lines of code should fix the issue.
The maxThread count property does not seem to be useful beyond 4. The sem object is initialized to accept only 4 maximum concurrent entries. All code that actually executes an item must go through this semaphore. So you've effectively limited the maximum number of concurrent items to 4 regardless of how high maxThread is set.
Writing robust threaded code is not trivial. There are numerous thread-pools around that you might look at for reference, but also note that Parallel Extensions (available as CTP, or later in .NET 4.0) includes a lot of additional threading constructs out-of-the-box (in the TPL/CCR). For example, Parallel.For / Parallel.ForEach, which deal with work-stealing, and handling the available cores effectively.
For an example of a pre-rolled thread-pool, see Jon Skeet's CustomThreadPool here.
I think you can simply things considerably.
Here is a modified form (I didn't test the modifications) of the thread pool I use:
The only sync. primitive you need is a Monitor, locked on the thread pool. You don't need a semaphore, or the reset events.
internal class ThreadPool
{
private readonly Thread[] m_threads;
private readonly Queue<Action> m_queue;
private bool m_shutdown;
private object m_lockObj;
public ThreadPool(int numberOfThreads)
{
Util.Assume(numberOfThreads > 0, "Invalid thread count!");
m_queue = new Queue<Action>();
m_threads = new Thread[numberOfThreads];
m_lockObj = new object();
lock (m_lockObj)
{
for (int i = 0; i < numberOfWriteThreads; ++i)
{
m_threads[i] = new Thread(ThreadLoop);
m_threads[i].Start();
}
}
}
public void Shutdown()
{
lock (m_lockObj)
{
m_shutdown = true;
Monitor.PulseAll(m_lockObj);
if (OnShuttingDown != null)
{
OnShuttingDown();
}
}
foreach (var thread in m_threads)
{
thread.Join();
}
}
public void Enqueue(Action a)
{
lock (m_lockObj)
{
m_queue.Enqueue(a);
Monitor.Pulse(m_lockObj);
}
}
private void ThreadLoop()
{
Monitor.Enter(m_lockObj);
while (!m_shutdown)
{
if (m_queue.Count == 0)
{
Monitor.Wait(m_lockObj);
}
else
{
var a = m_queue.Dequeue();
Monitor.Pulse(m_lockObj);
Monitor.Exit(m_lockObj);
try
{
a();
}
catch (Exception ex)
{
Console.WriteLine("An unhandled exception occured!\n:{0}", ex.Message, null);
}
Monitor.Enter(m_lockObj);
}
}
Monitor.Exit(m_lockObj);
}
}
You should probally use the built in thread pool. When running your code I noticed that your spining up a bunch of threads but since the queue count is <1 you just exit, this continues until the queue is actually populated then your next thread processes everything. This is a very expensive process. You should only spin up threads if you have something to do.

Categories

Resources