I appear to have a memory leak in this piece of code. It is a console app, which creates a couple of classes (WorkerThread), each of which writes to the console at specified intervals. The Threading.Timer is used to do this, hence writing to the console is performed in a separate thread (the TimerCallback is called in a seperate thread taken from the ThreadPool). To complicate matters, the MainThread class hooks on to the Changed event of the FileSystemWatcher; when the test.xml file changes, the WorkerThread classes are recreated.
Each time the file is saved, (each time that the WorkerThread and therefore the Timer is recreated), the memory in the Task Manager increases (Mem Usage, and sometimes also VM Size); furthermore, in .Net Memory Profiler (v3.1), the Undisposed Instances of the WorkerThread class increases by two (this may be a red herring though, because I've read that .Net Memory Profiler had a bug whereby it struggled to detect disposed classes.
Anyway, here's the code - does anyone know what's wrong?
EDIT: I've moved the class creation out of the FileSystemWatcher.Changed event handler, meaning that the WorkerThread classes are always being created in the same thread. I've added some protection to the static variables. I've also provided threading information to show more clearly what's going on, and have been interchanging using the Timer with using an explicit Thread; however, the memory is still leaking! The Mem Usage increases slowly all the time (is this simply due to extra text in the console window?), and the VM Size increases when I change the file. Here is the latest version of the code:
EDIT This appears to be primarily a problem with the console using up memory, as you write to it. There is still a problem with explicitly written Threads increasing the memory usage. See my answer below.
class Program
{
private static List<WorkerThread> threads = new List<WorkerThread>();
static void Main(string[] args)
{
MainThread.Start();
}
}
public class MainThread
{
private static int _eventsRaised = 0;
private static int _eventsRespondedTo = 0;
private static bool _reload = false;
private static readonly object _reloadLock = new object();
//to do something once in handler, though
//this code would go in onStart in a windows service.
public static void Start()
{
WorkerThread thread1 = null;
WorkerThread thread2 = null;
Console.WriteLine("Start: thread " + Thread.CurrentThread.ManagedThreadId);
//watch config
FileSystemWatcher watcher = new FileSystemWatcher();
watcher.Path = "../../";
watcher.Filter = "test.xml";
watcher.EnableRaisingEvents = true;
//subscribe to changed event. note that this event can be raised a number of times for each save of the file.
watcher.Changed += (sender, args) => FileChanged(sender, args);
thread1 = new WorkerThread("foo", 10);
thread2 = new WorkerThread("bar", 15);
while (true)
{
if (_reload)
{
//create our two threads.
Console.WriteLine("Start - reload: thread " + Thread.CurrentThread.ManagedThreadId);
//wait, to enable other file changed events to pass
Console.WriteLine("Start - waiting: thread " + Thread.CurrentThread.ManagedThreadId);
thread1.Dispose();
thread2.Dispose();
Thread.Sleep(3000); //each thread lasts 0.5 seconds, so 3 seconds should be plenty to wait for the
//LoadData function to complete.
Monitor.Enter(_reloadLock);
thread1 = new WorkerThread("foo", 10);
thread2 = new WorkerThread("bar", 15);
_reload = false;
Monitor.Exit(_reloadLock);
}
}
}
//this event handler is called in a separate thread to Start()
static void FileChanged(object source, FileSystemEventArgs e)
{
Monitor.Enter(_reloadLock);
_eventsRaised += 1;
//if it was more than a second since the last event (ie, it's a new save), then wait for 3 seconds (to avoid
//multiple events for the same file save) before processing
if (!_reload)
{
Console.WriteLine("FileChanged: thread " + Thread.CurrentThread.ManagedThreadId);
_eventsRespondedTo += 1;
Console.WriteLine("FileChanged. Handled event {0} of {1}.", _eventsRespondedTo, _eventsRaised);
//tell main thread to restart threads
_reload = true;
}
Monitor.Exit(_reloadLock);
}
}
public class WorkerThread : IDisposable
{
private System.Threading.Timer timer; //the timer exists in its own separate thread pool thread.
private string _name = string.Empty;
private int _interval = 0; //thread wait interval in ms.
private Thread _thread = null;
private ThreadStart _job = null;
public WorkerThread(string name, int interval)
{
Console.WriteLine("WorkerThread: thread " + Thread.CurrentThread.ManagedThreadId);
_name = name;
_interval = interval * 1000;
_job = new ThreadStart(LoadData);
_thread = new Thread(_job);
_thread.Start();
//timer = new Timer(Tick, null, 1000, interval * 1000);
}
//this delegate instance does NOT run in the same thread as the thread that created the timer. It runs in its own
//thread, taken from the ThreadPool. Hence, no need to create a new thread for the LoadData method.
private void Tick(object state)
{
//LoadData();
}
//Loads the data. Called from separate thread. Lasts 0.5 seconds.
//
//private void LoadData(object state)
private void LoadData()
{
while (true)
{
for (int i = 0; i < 10; i++)
{
Console.WriteLine(string.Format("Worker thread {0} ({2}): {1}", _name, i, Thread.CurrentThread.ManagedThreadId));
Thread.Sleep(50);
}
Thread.Sleep(_interval);
}
}
public void Stop()
{
Console.WriteLine("Stop: thread " + Thread.CurrentThread.ManagedThreadId);
//timer.Dispose();
_thread.Abort();
}
#region IDisposable Members
public void Dispose()
{
Console.WriteLine("Dispose: thread " + Thread.CurrentThread.ManagedThreadId);
//timer.Dispose();
_thread.Abort();
}
#endregion
}
You have two issues, both separate:
In Watcher.Changed's handler you call Thread.Sleep(3000);
This is poor behaviour in a callback of a thread you do not own (since it is being supplied by the pool owned/used by the watcher. This is not the source of your problem though. This it in direct violation of the guidelines for use
You use statics all over the place which is horrible, and has likely led you into this problem:
static void test()
{
_eventsRaised += 1;
//if it was more than a second since the last event (ie, it's a new save), then wait for 3 seconds (to avoid
//multiple events for the same file save) before processing
if (DateTime.Now.Ticks - _lastEventTicks > 1000)
{
Thread.Sleep(3000);
_lastEventTicks = DateTime.Now.Ticks;
_eventsRespondedTo += 1;
Console.WriteLine("File changed. Handled event {0} of {1}.", _eventsRespondedTo, _eventsRaised);
//stop threads and then restart them
thread1.Stop();
thread2.Stop();
thread1 = new WorkerThread("foo", 20);
thread2 = new WorkerThread("bar", 30);
}
}
This callback can fire repeatedly on multiple different threads (it uses the system thread pool for this) You code assumes that only one thread will ever execute this method at a time since threads can be created but not not stopped.
Imagine: thread A and B
A thread1.Stop()
A thread2.Stop()
B thread1.Stop()
B thread2.Stop()
A thread1 = new WorkerThread()
A thread2 = new WorkerThread()
B thread1 = new WorkerThread()
B thread2 = new WorkerThread()
You now have 4 WorkerThread instances on the heap but only two variables referencing them, the two created by A have leaked. The event handling and callback registration with the timer means that theses leaked WorkerThreads are kept alive (in the GC sense) despite you having no reference to them in your code. they stay leaked for ever.
There are other flaws in the design but this is a critical one.
No, no, no, no, no, no, no. Never use Thread.Abort().
Read the MSDN docs on it.
The thread is not guaranteed to abort immediately, or at all. This situation can occur if a thread does an unbounded amount of computation in the finally blocks that are called as part of the abort procedure, thereby indefinitely delaying the abort. To wait until a thread has aborted, you can call the Join method on the thread after calling the Abort method, but there is no guarantee the wait will end.
The correct way to end a thread is to signal to it that it should end, then call Join() on that thread. I usually do something like this (pseudo-code):
public class ThreadUsingClass
{
private object mSyncObject = new object();
private bool mKilledThread = false;
private Thread mThread = null;
void Start()
{
// start mThread
}
void Stop()
{
lock(mSyncObject)
{
mKilledThread = true;
}
mThread.Join();
}
void ThreadProc()
{
while(true)
{
bool isKilled = false;
lock(mSyncObject)
{
isKilled = mKilledThread;
}
if (isKilled)
return;
}
}
}
Well, having had some time to look into this again, it appears that the memory leak is a bit of a red herring. When I stop writing to the console, the memory usage stops increasing.
However, there is a remaining issue in that every time I edit the test.xml file (which fires the Changed event on the FileSystemWatcher, whose handler sets flags that cause the worker classes to be renewed and therefore threads/timers to be stopped), the memory increases by about 4K, providing that I am using explicit Threads, rather Timers. When I use a Timer, there is no problem. But, given that I would rather use a Timer than a Thread, this is no longer an issue to me, but I would still be interested in why it is occuring.
See the new code below. I've created two classes - WorkerThread and WorkerTimer, one of which uses Threads and the other Timers (I've tried two Timers, the System.Threading.Timer and the System.Timers.Timer. with the Console output switched on, you can see the difference that this makes with regards to which thread the tick event is raised on). Just comment/uncomment the appropriate lines of MainThread.Start in order to use the required class. For the reason above, it is recommended that the Console.WriteLine lines are commented out, except when you want to check that everything is working as expected.
class Program
{
static void Main(string[] args)
{
MainThread.Start();
}
}
public class MainThread
{
private static int _eventsRaised = 0;
private static int _eventsRespondedTo = 0;
private static bool _reload = false;
private static readonly object _reloadLock = new object();
//to do something once in handler, though
//this code would go in onStart in a windows service.
public static void Start()
{
WorkerThread thread1 = null;
WorkerThread thread2 = null;
//WorkerTimer thread1 = null;
//WorkerTimer thread2 = null;
//Console.WriteLine("Start: thread " + Thread.CurrentThread.ManagedThreadId);
//watch config
FileSystemWatcher watcher = new FileSystemWatcher();
watcher.Path = "../../";
watcher.Filter = "test.xml";
watcher.EnableRaisingEvents = true;
//subscribe to changed event. note that this event can be raised a number of times for each save of the file.
watcher.Changed += (sender, args) => FileChanged(sender, args);
thread1 = new WorkerThread("foo", 10);
thread2 = new WorkerThread("bar", 15);
//thread1 = new WorkerTimer("foo", 10);
//thread2 = new WorkerTimer("bar", 15);
while (true)
{
if (_reload)
{
//create our two threads.
//Console.WriteLine("Start - reload: thread " + Thread.CurrentThread.ManagedThreadId);
//wait, to enable other file changed events to pass
//Console.WriteLine("Start - waiting: thread " + Thread.CurrentThread.ManagedThreadId);
thread1.Dispose();
thread2.Dispose();
Thread.Sleep(3000); //each thread lasts 0.5 seconds, so 3 seconds should be plenty to wait for the
//LoadData function to complete.
Monitor.Enter(_reloadLock);
//GC.Collect();
thread1 = new WorkerThread("foo", 5);
thread2 = new WorkerThread("bar", 7);
//thread1 = new WorkerTimer("foo", 5);
//thread2 = new WorkerTimer("bar", 7);
_reload = false;
Monitor.Exit(_reloadLock);
}
}
}
//this event handler is called in a separate thread to Start()
static void FileChanged(object source, FileSystemEventArgs e)
{
Monitor.Enter(_reloadLock);
_eventsRaised += 1;
//if it was more than a second since the last event (ie, it's a new save), then wait for 3 seconds (to avoid
//multiple events for the same file save) before processing
if (!_reload)
{
//Console.WriteLine("FileChanged: thread " + Thread.CurrentThread.ManagedThreadId);
_eventsRespondedTo += 1;
//Console.WriteLine("FileChanged. Handled event {0} of {1}.", _eventsRespondedTo, _eventsRaised);
//tell main thread to restart threads
_reload = true;
}
Monitor.Exit(_reloadLock);
}
}
public class WorkerTimer : IDisposable
{
private System.Threading.Timer _timer; //the timer exists in its own separate thread pool thread.
//private System.Timers.Timer _timer;
private string _name = string.Empty;
/// <summary>
/// Initializes a new instance of the <see cref="WorkerThread"/> class.
/// </summary>
/// <param name="name">The name.</param>
/// <param name="interval">The interval, in seconds.</param>
public WorkerTimer(string name, int interval)
{
_name = name;
//Console.WriteLine("WorkerThread constructor: Called from thread " + Thread.CurrentThread.ManagedThreadId);
//_timer = new System.Timers.Timer(interval * 1000);
//_timer.Elapsed += (sender, args) => LoadData();
//_timer.Start();
_timer = new Timer(Tick, null, 1000, interval * 1000);
}
//this delegate instance does NOT run in the same thread as the thread that created the timer. It runs in its own
//thread, taken from the ThreadPool. Hence, no need to create a new thread for the LoadData method.
private void Tick(object state)
{
LoadData();
}
//Loads the data. Called from separate thread. Lasts 0.5 seconds.
//
private void LoadData()
{
for (int i = 0; i < 10; i++)
{
//Console.WriteLine(string.Format("Worker thread {0} ({2}): {1}", _name, i, Thread.CurrentThread.ManagedThreadId));
Thread.Sleep(50);
}
}
public void Stop()
{
//Console.WriteLine("Stop: called from thread " + Thread.CurrentThread.ManagedThreadId);
//_timer.Stop();
_timer.Change(Timeout.Infinite, Timeout.Infinite);
//_timer = null;
//_timer.Dispose();
}
#region IDisposable Members
public void Dispose()
{
//Console.WriteLine("Dispose: called from thread " + Thread.CurrentThread.ManagedThreadId);
//_timer.Stop();
_timer.Change(Timeout.Infinite, Timeout.Infinite);
//_timer = null;
//_timer.Dispose();
}
#endregion
}
public class WorkerThread : IDisposable
{
private string _name = string.Empty;
private int _interval = 0; //thread wait interval in ms.
private Thread _thread = null;
private ThreadStart _job = null;
private object _syncObject = new object();
private bool _killThread = false;
public WorkerThread(string name, int interval)
{
_name = name;
_interval = interval * 1000;
_job = new ThreadStart(LoadData);
_thread = new Thread(_job);
//Console.WriteLine("WorkerThread constructor: thread " + _thread.ManagedThreadId + " created. Called from thread " + Thread.CurrentThread.ManagedThreadId);
_thread.Start();
}
//Loads the data. Called from separate thread. Lasts 0.5 seconds.
//
//private void LoadData(object state)
private void LoadData()
{
while (true)
{
//check to see if thread it to be stopped.
bool isKilled = false;
lock (_syncObject)
{
isKilled = _killThread;
}
if (isKilled)
return;
for (int i = 0; i < 10; i++)
{
//Console.WriteLine(string.Format("Worker thread {0} ({2}): {1}", _name, i, Thread.CurrentThread.ManagedThreadId));
Thread.Sleep(50);
}
Thread.Sleep(_interval);
}
}
public void Stop()
{
//Console.WriteLine("Stop: thread " + _thread.ManagedThreadId + " called from thread " + Thread.CurrentThread.ManagedThreadId);
//_thread.Abort();
lock (_syncObject)
{
_killThread = true;
}
_thread.Join();
}
#region IDisposable Members
public void Dispose()
{
//Console.WriteLine("Dispose: thread " + _thread.ManagedThreadId + " called from thread " + Thread.CurrentThread.ManagedThreadId);
//_thread.Abort();
lock (_syncObject)
{
_killThread = true;
}
_thread.Join();
}
#endregion
}
Well you never actually call dispose on the WorkerThread instances.
The actual worker threads aren't being disposed when the watched file event occurs. I think I would rewrite this so that new threads aren't created, but they are reinitialized. Instead of calling Stop and recreating the threads, call a new Restart method that just stops and resets the timer.
You never terminate the threads - use something like Process Explorer to check whether the thread count is increasing as well as memory. Add a call to Abort() in your Stop() method.
Edit: You did, thanks.
Related
I need to access the TimerData.textSet field to change the text displayed from a another thread that controls the timing. But a InvalidOperationException is thrown. Is there a solution to this problem.
namespace Scilca.KBL
{
public class TimerData
{
public Run textSet;
public ProgressBar statusTimer;
public TimerData(Run run, ProgressBar statusTimer)
{
textSet = run;
this.statusTimer = statusTimer;
}
}
/// <summary>
/// Interaction logic for KBLSessionWindow.xaml
/// </summary>
public partial class KBLSessionWindow : Window
{
private int leftTime = 60;
private static Run run;
private static ProgressBar progressBar;
public int TimeLeftOver
{
get { return leftTime; }
}
public KBLSessionWindow()
{
InitializeComponent();
run = runSecondTime;
progressBar = timeProgress;
Thread timerThread = new Thread(new ParameterizedThreadStart(startTimer));
timerThread.Start(new TimerData(run, progressBar));
}
public void startTimer(Object td)
{
TimerData timerData = (TimerData)td;
int time = 60;
while (true)
{
Thread.Sleep(1000);
time -= 1;
if (time == 0)
time = 60;
Console.WriteLine(timerData.textSet.Text);
timerData.textSet.Text = time.ToString(); // InvalidOperationException
timerData.statusTimer.Value = time * 100 / 60;
}
}
}
}
It looks like you are trying to access an UI element from a non-ui thread.
Whenever you update your UI elements from a thread other than the main thread, you need to use:
Application.Current.Dispatcher.Invoke(() =>
{
// your code here.
timerData.textSet.Text = time.ToString();
timerData.statusTimer.Value = time * 100 / 60;
});
otherwise you will get a System.InvalidOperationException
From MSDN:
An System.InvalidOperationException is thrown when a method of an object is called when the state of the object cannot support the method call. The exception is also thrown when a method attempts to manipulate the UI from a thread that is not the main or UI thread.
Unrelated to your issue, I would suggest using a timer (see below) instead of creating a new thread every time, this would be more efficient since Timer is using the Thread Pool:
Timer myTimer = new Timer();
myTimer.Elapsed += new ElapsedEventHandler(DisplayTimeEvent);
myTimer.Interval = 1000; // 1000 ms is one second
myTimer.Start();
public static void DisplayTimeEvent(object source, ElapsedEventArgs e)
{
// code here will run every second
}
I have a program that is continuously running.
When I start the program, I have a BackgroundWorker (InitialStart.cs):
BackgroundWorker recordingSyncWorker = new BackgroundWorker();
recordingSyncWorker.DoWork += new DoWorkEventHandler(RecordingSyncCheck);
recordingSyncWorker.WorkerSupportsCancellation = false;
recordingSyncWorker.WorkerReportsProgress = false;
recordingSyncWorker.RunWorkerAsync();
void RecordingSyncCheck(object sender, DoWorkEventArgs e)
{
cHandler ch = new cHandler();
Timer theTimer;
int seconds = 1;
if (taskSeconds != null && taskSeconds != "")
seconds = Convert.ToInt32(taskSeconds);
int milliseconds = seconds * 1000;
theTimer = new Timer(10000);
theTimer.Elapsed += new ElapsedEventHandler(ch.SyncMethod);
theTimer.Interval = milliseconds;
theTimer.Enabled = true;
}
And I have two methods in another class (cHandler.cs):
SyncMethod(object source, System.Timers.ElapsedEventArgs e)
{
// random code
}
private string SDCardCheck(object whatever)
{
//more code
}
SDCardCheck gets called thru WCF, so it's like there is another instance of cHandler.cs running. Is there a way for me to pause the BackgroundWorker when I call SDCardCheck?
Don't use a BackgroundWorker just to start a timer. Starting a timer is not a long running operation; it can be done directly from the UI thread.
Your other method can disable the timer to stop it from firing, and then enable it again to allow it to continue firing, in order to effectively pause its execution.
I'm using a System.Timers.Timer in my application. Every second I run a function which does some job. The thing is, this function can block for some little time (it reads then processes a large file from disk). I want to start that function only if its previous "execution instance" has completed. I thought I could achieve this with a Mutex:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Timers;
using System.Threading;
namespace ConsoleApplication1
{
class Program
{
static Mutex TimerMut = new Mutex(false);
public static void Main()
{
Thread TT = new Thread(new ThreadStart(delegate()
{
System.Timers.Timer oTimer = new System.Timers.Timer();
oTimer.Elapsed += new ElapsedEventHandler(Handler);
oTimer.Interval = 1000;
oTimer.Enabled = true;
}));
TT.Start();
Console.Read();
}
private static void Handler(object oSource,
ElapsedEventArgs oElapsedEventArgs)
{
TimerMut.WaitOne();
Console.WriteLine("foo");
Thread.Sleep(500); //simulate some work
Console.WriteLine("bar");
TimerMut.ReleaseMutex();
}
}
}
That doesn't work, "foos" still appear every second. How can I achieve this?
EDIT: You're right, it makes no sense to start a new thread to handle this. I thought only System.Threading.Timer is launched in a separate thread.
I'm not sure why you are using a new thread to start the timer, since timers run on their own thread, but here's a method that works. Simply turn the timer off until you are done with the current interval.
static System.Timers.Timer oTimer
public static void Main()
{
oTimer = new System.Timers.Timer();
oTimer.Elapsed += new ElapsedEventHandler(Handler);
oTimer.Interval = 1000;
oTimer.Enabled = true;
}
private void Handler(object oSource, ElapsedEventArgs oElapsedEventArgs)
{
oTimer.Enabled = false;
Console.WriteLine("foo");
Thread.Sleep(5000); //simulate some work
Console.WriteLine("bar");
oTimer.Enabled = true;
}
If you want to skip the tick if another is already working you can do this.
private readonly object padlock = new object();
private void SomeMethod()
{
if(!Monitor.TryEnter(padlock))
return;
try
{
//Do heavy work
}
finally
{
Monitor.Exit(padlock);
}
}
Easiest way I know of to do this kind of thing:
internal static volatile bool isRunning;
public static void Main()
{
Thread TT = new Thread(new ThreadStart(delegate()
{
System.Timers.Timer oTimer = new System.Timers.Timer();
oTimer.Elapsed += new ElapsedEventHandler(Handler);
oTimer.Interval = 1000;
oTimer.Enabled = true;
}));
TT.Start();
}
private void Handler(object oSource,
ElapsedEventArgs oElapsedEventArgs)
{
if(isRunning) return;
isRunning = true;
try
{
Console.WriteLine("foo");
Thread.Sleep(500); //simulate some work
Console.WriteLine("bar");
}
finally { isRunning = false; }
}
The handler still runs, but the very first thing it does is make sure that another handler isn't running, and if one is, it stops immediately.
For timers executing handlers more quickly (like 3-4 times a second), this has the possibility to race; two threads could proceed past the guard clause before one of them sets the bit. You can avoid this with a couple of lock statements, similar to a Mutex or Monitor:
static object syncObj = new object();
private void Handler(object oSource,
ElapsedEventArgs oElapsedEventArgs)
{
lock(syncObj)
{
if(isRunning) return;
isRunning = true;
}
try
{
Console.WriteLine("foo");
Thread.Sleep(500); //simulate some work
Console.WriteLine("bar");
}
finally { lock(syncObj) { isRunning = false; } }
}
This will ensure that only one thread can ever be examining or modifying isRunning, and as isRunning is marked volatile, the CLR won't cache its value as part of each thread's state for performance; each thread has to look at exactly the same memory location to examine or change the value.
You can follow the following pattern to skip doing the indicated work if another invocation of this method is still running:
private int isWorking = 0;
public void Foo()
{
if (Interlocked.Exchange(ref isWorking, 1) == 0)
{
try
{
//Do work
}
finally
{
Interlocked.Exchange(ref isWorking, 0);
}
}
}
The approach that you were using with a Mutex will result in addition ticks waiting for earlier ticks to finish, not skipping invocations when another is still running, which is what you said you wanted. (When dealing with timers like this its common to want to skip such ticks, not wait. If your tick handlers regularly take too long you end up with a giant queue of waiting handlers.)
In a win form application, I have an array of threads which are started like this:
bool stop = false;
Thread[] threads = new Thread[10];
for (int i = 0; i < threads.Length; i++)
threads[i] = new Thread(new ThreadStart(Job));
// How to make sure all threads have exited, when the boolean = false
void Job()
{
while (!stop)
// Do something
}
Now if user press STOP, the boolean value for stop will set to true, so threads exit the Job method one after another. How can I make sure all threads are exited?
NOTE: I need traditional threading for my case and TaskLibrary doesn't fit my scenario.
Use the Join method to check if all threads have stopped.
foreach (var t in threads)
{
t.Join();
}
Have you thought about using BackgroundWorkers instead? You said "traditional threads"..I'm not exactly sure what you mean so I don't know if this is a valid proposal or not, but here it is anyways in case Join() doesn't solve your problem
BackgroundWorker[] workers = new BackgroundWorker[10];
bool allThreadsDone = false;
// initialize BackgroundWorkers
for (int i = 0; i < 10; i++)
{
workers[i] = new BackgroundWorker();
workers[i].WorkerSupportsCancellation = true;
workers[i].RunWorkerCompleted +=
new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
workers[i].DoWork += new DoWorkEventHandler(AlgorithmsUI_DoWork);
workers[i].RunWorkerAsync();
}
// thread entry point..DoWork is fired when RunWorkerAsync is called
void AlgorithmsUI_DoWork(object sender, DoWorkEventArgs e)
{
while (!stop)
// do something
}
// this event is fired when the BGW finishes execution
private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
bool threadsStillRunning = false;
foreach (BackgroundWorker worker in workers)
{
if (worker.IsBusy)
{
threadsStillRunning = true;
break;
}
}
if (!threadsStillRunning)
allThreadsDone = true;
}
protected override OnFormClosing(FormClosingEventArgs e)
{
if (!allThreadsDone)
{
e.Cancel = true;
MessageaBox.Show("Threads still running!");
}
}
This should prevent your form from closing if any threads are still running.
I'm not sure if this is what you're looking for, but here's a simple solution I used back in .NET 3.0 to make sure a large but deterministic number of threads had completed before continuing:
Global:
AutoResetEvent threadPoolComplete = new AutoResetEvent(false);
static int numThreadsToRun;
As you activate the threads:
numThreadsToRun = [number of threads];
[start your threads];
threadPoolComplete.WaitOne();
At the end of each thread's code:
if (Interlocked.Decrement(ref numThreadsToRun) == 0)
{
threadPoolComplete.Set();
}
In this code I want to Pause/Resume a thread using an AutoResetEvent and a bool variable.
Is is possible to Pause whithout testing each time (in for loop of Work()) if blocked==true?
Testing of "blocked" variable needs locking also and i think this is time consuming.
class MyClass
{
AutoResetEvent wait_handle = new AutoResetEvent();
bool blocked = false;
void Start()
{
Thread thread = new Thread(Work);
thread.Start();
}
void Pause()
{
blocked = true;
}
void Resume()
{
blocked = false;
wait_handle.Set();
}
private void Work()
{
for(int i = 0; i < 1000000; i++)
{
if(blocked)
wait_handle.WaitOne();
Console.WriteLine(i);
}
}
}
Yes, you can avoid the test you are performing by using a ManualResetEvent.
The ManualResetEvent will let your thread pass as long as it is "set" (signalled), but unlike the AutoResetEvent you had previously, it doesn't automatically reset as a thread passes it. This means you can leave it Set to allow work in your loop, and can Reset it to pause:
class MyClass
{
// set the reset event to be signalled initially, thus allowing work until pause is called.
ManualResetEvent wait_handle = new ManualResetEvent (true);
void Start()
{
Thread thread = new Thread(Work);
thread.Start();
}
void Pause()
{
wait_handle.Reset();
}
void Resume()
{
wait_handle.Set();
}
private void Work()
{
for(int i = 0; i < 1000000; i++)
{
// as long as this wait handle is set, this loop will execute.
// as soon as it is reset, the loop will stop executing and block here.
wait_handle.WaitOne();
Console.WriteLine(i);
}
}
}