I am writing an application that depends on fast image manipulation. It might sound strange but I'm doing this C# and not in C++. So far this has not been a limitation, I can process an image realtime. While I do quite some complex things with the image and I do this under 30ms.
I changed the program to make sure that the image stream would never queue
by simply checking a boolean to check if a current frame is not being processed. Normally this wouldn't happen, but in some cases it did. For example when run the app in VS2010 debug mode, or when the PC is doing also other heavy tasks, and has less CPU resources.
In such case I would like to skip new frame processing, so processing them won't queue. In such cases it would be better to just work with last known data which is still being processed, and thus waiting would be the fastest method then to retrieve an answer.
So I started with something like:
private void Camera_FrameReady(object Sender, ImageEvent e)
{
if (!IsImageReady) return; // global var
IsImageReady = false;
//... do stuff with the image
IsImageReady=true;
}
This didn't workout, as I had hoped. And I think it has to do with the threading nature of events within the C# compiler. So then I tried to resolve it by de-registering and re-registering the Camera_FrameReady ready, but the camera takes to much time to restart, so that didn't workout.
Strangely now it seams to work with the code below but I'm not sure why it does.
private void Camera_FrameReady(object Sender, ImageEvent e)
{
Update_Image(e)
}
private void Update_Image(e)
{
if (!IsImageReady) return; // global var
IsImageReady = false;
//... do stuff with the image
IsImageReady=true;
}
This makes me wonder about how C# gets compiled. Does it work like that whenever Camera_FrameReady is called it has a "world view" of the current global values? Or that global variables are only updated after the processing of an event?
The first thing came in my head is that the Camera_FrameReady event blocks the acquisition thread. But that doesn't explain why the second solution works..
So if you want to process the images parallel to the acquisition thread, you should create a new thread for processing.
For example: When there is a new image, check if the processing thread is busy. If the processing thread is busy, you shouldn't wait or queue (like you wanted) but just skip this image. If the processing thread is waiting for work, store the image in a 'global' variable, so the processing thread can access it and signal the processing thread.
I made an example for you: (pseudo)
// the thread for the processing.
private Thread _processingThread;
// a signal to check if the workerthread is busy with an image
private ManualResetEvent _workerThreadIsBusy = new ManualResetEvent(false);
// request for terminating
private ManualResetEvent _terminating = new ManualResetEvent(false);
// confirm terminated
private ManualResetEvent _terminated = new ManualResetEvent(false);
// store the current image.
private Image _myImage;
// event callback for new images
private void Camera_FrameReady(object Sender, ImageEvent e)
{
// is the workerthread already processing an image? return.. (skip this image)
if (_workerThreadIsBusy.WaitOne(0))
return; // skip frame.
//create a 'global' ref so the workerthread can access it.
/* BE CAREFULL HERE. You might experience trouble with the instance of this image.
* You are creating another reference to the SAME instance of the image
* to process on another thread. When the Camera is reusing this
* image (for speed), the image might screwed-up. In that case,
* you have to create a copy!
* (personally I would reuse the image which makes the image not available outside the event callback) */
_myImage = e.Image;
// signal the workerthread, so it starts processing the current image.
_workerThreadIsBusy.Set();
}
private void ImageProcessingThread()
{
var waitHandles = new WaitHandle[] { _terminating, _workerThreadIsBusy };
var run = true;
while (run)
{
switch (EventWaitHandle.WaitAny(waitHandles))
{
case 0:
// terminating.
run = false;
break;
case 1:
// process _myImage
ProcessImage(_myImage);
_workerThreadIsBusy.Reset();
break;
}
}
_terminated.Set();
}
private void ProcessImage(Image _myImage)
{
// whatever...
}
// constructor
public MyCameraProcessor()
{
// define the thread.
_processingThread = new Thread(ImageProcessingThread);
_processingThread.Start();
}
public void Dispose()
{
_terminating.Set();
_terminated.WaitOne();
}
}
Your code is not multithreading safe
if (!IsImageReady) return; // global var
IsImageReady = false;
//... do stuff with the image
IsImageReady=true;
2 threads can read IsImageReady at the same time, see that it is true and both set it then to false. You might also get problems if the processor is reading IsImageReady from cache and not from memory. You can avoid these kind of problems with the Interlocked class, which reads and changes the value in one operation. It also ensures that the cache doesn't cause problems.
private int IsImageReady= 0;
private void Camera_FrameReady(object Sender, ImageEvent e){
int wasImageReady = Interlocked.Exchange(ref IsImageReady, 1);
if (wasImageReady ==1) return;
//do something
IsImageReady= 0;
}
}
Although I am not sure if that is your only problem. You might have also others. To be sure, you have to debug your code properly, which is very difficult when it involves multithreading. Read my article Codeproject: Debugging multithreaded code in real time how you can do it.
Related
I have spent a few days now finding a bug that freezes my companies application. The dreaded UserPreferenceChanged UI freeze. It's not a complicated bug, but hard to find in a rather big application. There are quite a few articles about how this bug unfolds but not on how to put ones finger on the faulty code. I have put together a solution, in form of a logging mechanism from multiple older tickets and (i hope) improved a bit upon them. May it save some time for the next programmer with this problem.
How to recognize the bug?
The application freezes completely. Nothing more to be done than create a memory dump and then close it via TaskManager. If you open the dmp file in VisualStudio or WinDbg you might see a stack trace like this one
WaitHandle.InternalWaitOne
WaitHandle.WaitOne
Control.WaitForWaitHandle
Control.MarshaledInvoke
Control.Invoke
WindowsFormsSynchronizationContext.Send
System.EventInvokeInfo.Invoke
SystemEvents.RaiseEvent
SystemEvents.OnUserPreferenceChanged
SystemEvents.WindowProc
:
The important two lines here are "OnUserPreferenceChanged" and "WindowsFormsSynchronizationContext.Send"
What's the cause?
SynchronizationContext was introduced with .NET2 to generalize thread synchronization. It gives us methods like "BeginInvoke" and such.
The UserPreferenceChanged event is rather self explanatory. It will be triggered by the user changing his background, logging in or out, changing the Windows accent colors and lots of other actions.
If one creates a GUI control on a background thread a WindowsFormsSynchronizationContext is installed on said thread. Some GUI controls subscribe to the UserPreferenceChanged event when created or when using certain methods. If this event is triggered by the user the main thread sends a message to all subscribers and waits. In the described scenarion: a worker thread without a message loop! The application is frozen.
To find the cause of the freeze can be especially hard because the cause of the bug (creation of GUI element on a background thread) and the error state (application frozen) can be minutes apart. See this really good article for more details and a slightly different scenario. https://www.ikriv.com/dev/dotnet/MysteriousHang
Examples
How can one provoke this error for testing purposes?
Example 1
private void button_Click(object sender, EventArgs e)
{
new Thread(DoStuff).Start();
}
private void DoStuff()
{
using (var r = new RichTextBox())
{
IntPtr p = r.Handle; //do something with the control
}
Thread.Sleep(5000); //simulate some work
}
Not bad but not good either. If the UserPreferenceChanged event gets triggered in the few milliseconds you use the RichTextBox your application will freeze. Could happen, not very likely though.
Example 2
private void button_Click(object sender, EventArgs e)
{
new Thread(DoStuff).Start();
}
private void DoStuff()
{
var r = new RichTextBox();
IntPtr p = r.Handle; //do something with the control
Thread.Sleep(5000); //simulate some work
}
This is bad. The WindowsFormsSynchronizationContext gets not cleaned up because the RichTextBox does not get disposed. If the UserPreferenceChangedEvent occures while the thread lives your application will freeze.
Example 3
private void button_Click(object sender, EventArgs e)
{
Task.Run(() => DoStuff());
}
private void DoStuff()
{
var r = new RichTextBox();
IntPtr p = r.Handle; //do something with the control
}
This is a nightmare. Task.Run(..) will execute the work on a background thread on the threadpool. The WindowsFormsSynchronizationContext gets not cleaned up because the RichTextBox is not disposed. Threadpool threads are not cleaned up. This background thread now lurks in your threadpool just waiting for the UserPreferenceChanged event to freeze your application even long after your task has returned!
Conclusion: Risk is manageable when you know what you do. But whenever possible: avoid GUI Elements in a background thread!
How to deal with this bug?
I put together a solution from older tickets. Thanks very much to those guys!
WinForms application hang due to SystemEvents.OnUserPreferenceChanged event
https://codereview.stackexchange.com/questions/167013/detecting-ui-thread-hanging-and-logging-stacktrace
This solution starts a new thread that continuously tries to detect any threads which are subscribed to the OnUserPreferenceChanged Event and then provide a call stack that should tell you why that is.
public MainForm()
{
InitializeComponent();
new Thread(Observe).Start();
}
private void Observe()
{
new PreferenceChangedObserver().Run();
}
internal sealed class PreferenceChangedObserver
{
private readonly string _logFilePath = $"filePath\\FreezeLog.txt"; //put a better file path here
private BindingFlags _flagsStatic = BindingFlags.NonPublic | BindingFlags.Static;
private BindingFlags _flagsInstance = BindingFlags.NonPublic | BindingFlags.Instance;
public void Run() => CheckSystemEventsHandlersForFreeze();
private void CheckSystemEventsHandlersForFreeze()
{
while (true)
{
try
{
foreach (var info in GetPossiblyBlockingEventHandlers())
{
var msg = $"SystemEvents handler '{info.EventHandlerDelegate.Method.DeclaringType}.{info.EventHandlerDelegate.Method.Name}' could freeze app due to wrong thread. ThreadId: {info.Thread.ManagedThreadId}, IsThreadPoolThread:{info.Thread.IsThreadPoolThread}, IsAlive:{info.Thread.IsAlive}, ThreadName:{info.Thread.Name}{Environment.NewLine}{info.StackTrace}{Environment.NewLine}";
File.AppendAllText(_logFilePath, DateTime.Now.ToString("dd.MM.yyyy HH:mm:ss") + $": {msg}{Environment.NewLine}");
}
}
catch { }
}
}
private IEnumerable<EventHandlerInfo> GetPossiblyBlockingEventHandlers()
{
var handlers = typeof(SystemEvents).GetField("_handlers", _flagsStatic).GetValue(null);
if (!(handlers?.GetType().GetProperty("Values").GetValue(handlers) is IEnumerable handlersValues))
yield break;
foreach(var systemInvokeInfo in handlersValues.Cast<IEnumerable>().SelectMany(x => x.OfType<object>()).ToList())
{
var syncContext = systemInvokeInfo.GetType().GetField("_syncContext", _flagsInstance).GetValue(systemInvokeInfo);
//Make sure its the problematic type
if (!(syncContext is WindowsFormsSynchronizationContext wfsc))
continue;
//Get the thread
var threadRef = (WeakReference)syncContext.GetType().GetField("destinationThreadRef", _flagsInstance).GetValue(syncContext);
if (!threadRef.IsAlive)
continue;
var thread = (Thread)threadRef.Target;
if (thread.ManagedThreadId == 1) //UI thread
continue;
if (thread.ManagedThreadId == Thread.CurrentThread.ManagedThreadId)
continue;
//Get the event delegate
var eventHandlerDelegate = (Delegate)systemInvokeInfo.GetType().GetField("_delegate", _flagsInstance).GetValue(systemInvokeInfo);
//Get the threads call stack
string callStack = string.Empty;
try
{
if (thread.IsAlive)
callStack = GetStackTrace(thread)?.ToString().Trim();
}
catch { }
yield return new EventHandlerInfo
{
Thread = thread,
EventHandlerDelegate = eventHandlerDelegate,
StackTrace = callStack,
};
}
}
private static StackTrace GetStackTrace(Thread targetThread)
{
using (ManualResetEvent fallbackThreadReady = new ManualResetEvent(false), exitedSafely = new ManualResetEvent(false))
{
Thread fallbackThread = new Thread(delegate () {
fallbackThreadReady.Set();
while (!exitedSafely.WaitOne(200))
{
try
{
targetThread.Resume();
}
catch (Exception) {/*Whatever happens, do never stop to resume the target-thread regularly until the main-thread has exited safely.*/}
}
});
fallbackThread.Name = "GetStackFallbackThread";
try
{
fallbackThread.Start();
fallbackThreadReady.WaitOne();
//From here, you have about 200ms to get the stack-trace.
targetThread.Suspend();
StackTrace trace = null;
try
{
trace = new StackTrace(targetThread, true);
}
catch (ThreadStateException) { }
try
{
targetThread.Resume();
}
catch (ThreadStateException) {/*Thread is running again already*/}
return trace;
}
finally
{
//Just signal the backup-thread to stop.
exitedSafely.Set();
//Join the thread to avoid disposing "exited safely" too early. And also make sure that no leftover threads are cluttering iis by accident.
fallbackThread.Join();
}
}
}
private class EventHandlerInfo
{
public Delegate EventHandlerDelegate { get; set; }
public Thread Thread { get; set; }
public string StackTrace { get; set; }
}
}
Attention
1)This is a very ugly hack. It deals with threads in a very invasive way. It should never see a live customer system. I was already nervous deploying it to the customers test system.
2)If you get a logfile it might be very big. Any thread might cause hundreds of entries. Start at the oldest entries, fix it and repeat.(Because of the "tainted thread" scenario from Example 3 it might also contain false positives)
3)I am not sure about the performance impact of this hack. I assumed it would be very big. to my surprise it was almost not noteable. Might be different on other systems though
i know the common ways of cancelling a backgroundworker using eventwaithandles...
but i wanna know is that right to use a while loop to trap and pause working of a backgroundworker ? i coded like this :
Bool stop = false;
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
progressBar1.Minimum = 0;
progressBar1.Maximum = 100000;
progressBar1.Value = 0;
for (int i = 0; i < 100000; i++)
{
progressBar1.Value++;
if (i == 50000)
stop = true;
while (stop)
{ }
}
}
private void button1_Click(object sender, EventArgs e)
{
stop = !stop;
}
Did you try it? What happened? Was it what you wanted to happen? Did you notice your computer's fans speeding up, to handle all the heat from your CPU in a tight, "do-nothing" loop?
Fact is, you should not "pause" a background task in the first place; if you don't it to keep running, interrupt it. If you want to be able to resume later, provide a mechanism to allow that. Even having your thread blocked efficiently waiting on a WaitHandle object would be the wrong thing to do, because it wastes a thread pool thread.
The code you've posted here is about the worst way to implement "pausing". Instead of waiting on some synchronization object such as a WaitHandle, you have the current thread just loop without interrupting, constantly checking the value of a flag. Even ignoring the question of whether you're using volatile (the code example doesn't show that, but then it also wouldn't compile, so…), it's terrible to force a CPU core to do so much work and yet get nowhere.
Don't pause your BackgroundWorker.DoWork handler in the first place. Really. Just don't do that. But if you insist, then at least use some kind of waitable object instead of a "spin-wait" loop as in the example you've posted here.
Here's an example of how your code might work if you wanted to avoid altogether tying up a thread while "paused". First, don't use BackgroundWorker, because it doesn't have a graceful way to do this. Second, do use await…that does specifically what you want: it allows the current method to return, but without losing track of its progress. The method will resume executing when the thing it waited on indicates completion.
In the example below, I've tried to guess at what the code that calls RunWorkerAsync() looks like. Or rather, I just assumed you've got a button2, which when clicked you call that method to start your worker task. If this is not enough to get you pointed in the right direction, please improve your question by including a good, minimal, complete code example showing what you're actually doing.
// These fields will work together to provide a way for the thread to interrupt
// itself temporarily without actually using a thread at all.
private TaskCompletionSource<object> _pause;
private readonly object _pauseLock = new object();
private void button2_Click(object sender, DoWorkEventArgs e)
{
// Initialize ProgressBar. Note: in your version of the code, this was
// done in the DoWork event handler, but that handler isn't executed in
// the UI thread, and so accessing a UI object like progressBar1 is not
// a good idea. If you got away with it, you were lucky.
progressBar1.Minimum = 0;
progressBar1.Maximum = 100000;
progressBar1.Value = 0;
// This object will perform the duty of the BackgroundWorker's
// ProgressChanged event and ReportProgress() method.
Progress<int> progress = new Progress<int>(i => progressBar1.Value++);
// We do want the code to run in the background. Use Task.Run() to accomplish that
Task.Run(async () =>
{
for (int i = 0; i < 100000; i++)
{
progress.Report(i);
Task task = null;
// Locking ensures that the two threads which may be interacting
// with the _pause object do not interfere with each other.
lock (_pauseLock)
{
if (i == 50000)
{
// We want to pause. But it's possible we lost the race with
// the user, who also just pressed the pause button. So
// only allocate a new TCS if there isn't already one
if (_pause == null)
{
_pause = new TaskCompletionSource<object>();
}
}
// If by the time we get here, there's a TCS to wait on, then
// set our local variable for the Task to wait on. In this way
// we resolve any other race that might occur between the time
// we checked the _pause object and then later tried to wait on it
if (_pause != null)
{
task = _pause.Task;
}
}
if (task != null)
{
// This is the most important part: using "await" tells the method to
// return, but in a way that will allow execution to resume later.
// That is, when the TCS's Task transitions to the completed state,
// this method will resume executing, using any available thread
// in the thread pool.
await task;
// Once we resume execution here, reset the TCS, to allow the pause
// to go back to pausing again.
lock (_pauseLock)
{
_pause.Dispose();
_pause = null;
}
}
}
});
}
private void button1_Click(object sender, EventArgs e)
{
lock (_pauseLock)
{
// A bit more complicated than toggling a flag, granted. But it achieves
// the desirable goal.
if (_pause == null)
{
// Creates the object to wait on. The worker thread will look for
// this and wait if it exists.
_pause = new TaskCompletionSource<object>();
}
else if (!_pause.Task.IsCompleted)
{
// Giving the TCS a result causes its corresponding Task to transition
// to the completed state, releasing any code that might be waiting
// on it.
_pause.SetResult(null);
}
}
}
Note that the above is just as contrived as your original example. If all you had really was a simple single loop variable iterating from 0 to 100,000 and stopping halfway through, nothing nearly so complicated as the above would be required. You'd just store the loop variable in a data structure somewhere, exit the running task thread, and then when you want to resume, pass in the current loop variable value so the method can resume at the right index.
But I'm assuming your real-world example is not so simple. And the above strategy will work for any stateful processing, with the compiler doing all the heavy-lifting of storing away intermediate state for you.
Fairly frustrating since this seems to be well documented and the fact that I accomplished this before, but can't duplicate the same success. Sorry, I'll try to relate it all clearly.
Visual Studio, C# Form, One Main Form has text fields, among other widgets.
At one point we have the concept that we are "running" and therefore gathering data.
For the moment, I started a one second timer so that I can update simulated data into some fields. Eventually that one second timer will take the more rapid data and update it only once per second to the screen, that's the request for the application right now we update at the rate we receive which is a little over 70 Hz, they don't want it that way. In addition some other statistics will be computed and those should be the field updates. Therefore being simple I'm trying to just generate random data and update those fields at the 1 Hz rate. And then expand from that point.
Definition and management of the timer: (this is all within the same class MainScreen)
System.Timers.Timer oneSecondTimer;
public UInt32 run_time = 0;
public int motion = 5;
private void InitializeTimers()
{
this.oneSecondTimer = new System.Timers.Timer(1000);
this.oneSecondTimer.Elapsed += new System.Timers.ElapsedEventHandler(oneSecondTimer_elapsed);
}
public void start_one_second_timer()
{
run_time = 0;
oneSecondTimer.Enabled = true;
}
public void stop_one_second_timer()
{
oneSecondTimer.Enabled = false;
run_time = 0;
}
Random mot = new Random();
private void oneSecondTimer_elapsed(object source, System.Timers.ElapsedEventArgs e)
{
run_time++;
motion = mot.Next(1, 10);
this.oneSecondThread = new Thread(new ThreadStart(this.UpdateTextFields));
this.oneSecondThread.Start();
}
private void UpdateTextFields()
{
this.motionDisplay.Text = this.motion.ToString();
}
motionDisplay is just a textbox in my main form. I get the Invalid Operation Exception pointing me towards the help on how to make Thread-Safe calls. I also tried backgroundworker and end up with the same result. The details are that motionDisplay is accessed from a thread other than the thread it was created on.
So looking for some suggestions as to where my mistakes are.
Best Regards. I continue to iterate on this and will update if I find a solution.
Use a System.Forms.Timer rather than a System.Timers.Timer. It will fire it's elapsed event in the UI thread.
Don't create a new thread to update the UI; just do the update in the elapsed event handler.
Try this
private void UpdateTextFields()
{
this.BeginInvoke(new EventHandler((s,e)=>{
this.motionDisplay.Text = this.motion.ToString();
}));
}
This will properly marshall a call back to the main thread.
The thing with WinForm development is that all the controls are not thread safe. Even getting a property such as .Text from another thread can cause these type of errors to happen. To make it even more frustrating is that sometimes it will work at runtime and you won't get an exception, other times you will.
This is how I do it:
private delegate void UpdateMotionDisplayCallback(string text);
private void UpdateMotionDisplay(string text) {
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (this.motionDisplay.InvokeRequired) {
UpdateMotionDisplayCallback d = new UpdateMotionDisplayCallback(UpdateMotionDisplay);
this.Invoke(d, new object[] { text });
} else {
this.motionDisplay.Text = text;
}
}
When you want to update the text in motionDisplay just call:
UpdateMotionDisplay(this.motion.ToString())
I am confused with scenario which I have encountered with cross thread access. Here is what I am trying to do:
Main UI thread - menu item click I create a background worker and run it asynchronously
private void actionSubMenuItem_Click(object sender, EventArgs e)
{
ToolStripMenuItem itemSelected = (ToolStripMenuItem)sender;
ExecuteTheActionSelected(itemSelected.Text);
}
The method ExecuteTheActionSelected is as follows:
private void ExecuteTheActionSelected(string actionSelected)
{
BackgroundWorker localBackgroundWorker = new BackgroundWorker();
localBackgroundWorker.DoWork += new DoWorkEventHandler(localBackgroundWorker_DoWork);
localBackgroundWorker.RunWorkerAsync(SynchronizationContext.Current);
}
The localBackgroundWorker_DoWork has:
ActionExecutionHelper actionExecutioner = new ActionExecutionHelper()
actionExecutioner.Execute();
The Execute method in that class that has method invoker which infact invokes the event handler in UI thread:
public void Execute()
{
// ---- CODE -----
new MethodInvoker(ReadStdOut).BeginInvoke(null, null);
}
protected virtual void ReadStdOut()
{
string str;
while ((str = executionProcess.StandardOutput.ReadLine()) != null)
{
object sender = new object();
DataReceivedEventArgs e = new DataReceivedEventArgs(str);
outputDataReceived.Invoke(sender, e);
//This delegate invokes UI event handler
}
}
The UI event handler is as follows:
private void executionProcess_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
if (_dwExecuteAction != null)
{
_dwExecuteAction.ShowDataInExecutionWindow(e.Text);
}
}
Now here comes the cross thread issue:
public void ShowDataInExecutionWindow(string message)
{
if (rchtxtExecutionResults.InvokeRequired)
{
rchtxtExecutionResults.Invoke(new ShowDataExecutionDelegate(ShowDataInExecutionWindow), message);
}
else
{
this.rchtxtExecutionResults.AppendText(message + Environment.NewLine);
}
}
Here Invoke doesn't block the UI where as BeginInvoke blocks.
Please help me understand this scenario as i m confused a lot.
Yes, this is normal. The benefit you get out of Invoke() is that it blocks the worker thread. When you use BeginInvoke() the thread keeps motoring and issues invoke requests at a rate higher than the UI thread can handle. It depends on what you ask the UI thread to do but it starts to become a problem around 1000 invokes per second.
The UI thread stops being responsive in this scenario, it is constantly finding another invoke request back while it pumps the message loop and doesn't get around doing its regular duties anymore. Input and paint requests no longer get processed.
The clear source of the problem is the invoke request on every single line of output retrieved from the process. It is just generating them too quickly. You need to fix this by lowering the rate at which you invoke. There's a simple rule for that, you are only trying to keep a human occupied, invoking more than 25 times per second turns whatever you produce in but a blur to the eye. So buffer the lines and measure the amount of time that has passed since the last invoke call.
Also note that using Invoke() is an easy workaround but it isn't exactly guaranteed to work. It is a race, the worker thread could potentially always call the next Invoke() a wee bit earlier than the main thread re-entering the message loop and reading the next message. In which case you will still have the exact same problem.
Not exactly sure on the terminology here but here I go basically I have the main() thread of my application that starts and calls two threads, one sets up an event handler to wait for specific registry keys to change, while the other starts a timer to write any changes made to an xml file every 5 mins or so and runs continuously. The issue I have is that once the two methods called are initialized it goes back to main and ends the program. My relevant code sections can be found below, so any help would be appreciated:
static void Main(string[] args)
{
runner one = new runner();
runner two = new runner();
Thread thread1 = new Thread(new ThreadStart(one.TimerMeth));
Thread thread2 = new Thread(new ThreadStart(two.start));
thread1.Start();
thread2.Start();
thread1.Join();
thread2.Join();
}
public void TimerMeth()
{
System.Timers.Timer timer = new System.Timers.Timer();
timer.Elapsed += new ElapsedEventHandler(OnElapsedTime);
timer.Interval = 300000;
timer.Enabled = true;
}
private void OnElapsedTime(object source, ElapsedEventArgs e)
{
file write = new file();
write.write(RegKeys);
}
public void start()
{
if (File.Exists("C:\\test.xml"))
{
file load = new file();
RegKeys = load.read(RegKeys);
}
string hiveid = "HKEY_USERS";
WindowsIdentity identity = WindowsIdentity.GetCurrent();
string id = identity.User.ToString();
string key1 = id + "\\\\Software\\\\Microsoft\\\\Windows NT\\\\CurrentVersion\\\\Windows Messaging Subsystem\\\\Profiles\\\\Outlook\\\\0a0d020000000000c000000000000046";
List<string> value1 = new List<String> { "01020402", "test" };
valuechange = new RegistryValueChange(hiveid, key1, value1);
valuechange.RegistryValueChanged += new EventHandler<RegistryValueChangedEventArgs>(valuechange_RegistryValueChanged);
try
{
valuechange.Start();
}
catch
{
StreamWriter ut;
ut = File.AppendText("C:\\test.txt");
ut.WriteLine("error occured in starting management");
ut.Close();
}
file test = new file();
test.checkfile("C:\\test.xml");
}
void valuechange_RegistryValueChanged(object sender, RegistryValueChangedEventArgs e)
{
// deals with the returned values
}
Basically all the code works fine I've been testing it in a windows form application but now I need to run it in a standalone app with no interface in the background and need it to keep writing to the xml file and the change event to stay alive.
As you can expect, the Main() method is terminating because execution flows out of the Join() methods back to the main thread, and then terminates.
Either place loops in the methods TimerMeth() and start(), or more appropriately redesign the application into a Windows Service (as zac says).
You have a couple of issues.
Your first thread is simply creating a timer (which launches another thread). This thread is terminating very quickly, making your call to Join rather meaningless. What this thread should be doing is actually doing the waiting and the checking. You can easily adapt your code like this:
public void TimerMeth()
{
System.Timers.Timer timer = new System.Timers.Timer();
timer.Elapsed += new ElapsedEventHandler(OnElapsedTime);
timer.Interval = 300000;
timer.Enabled = true;
try
{
while(true)
{
OnElapsedTime(null, null); // you should change the signature
Thread.Sleep(30000);
}
}
catch(ThreadAbortException)
{
OnElapsedTime(null, null);
throw;
}
}
Obviously you should change the signature of OnElapsedTime to eliminate the parameters, since they aren't used here.
I have a feeling that something is amiss in the way that the file handling is being done, but given that I don't understand exactly what that code does, I'm going to refrain from commenting. What, exactly, is the purpose of the file? Where is RegKeys defined?
Try building this into a windows service.
This thread contains two suggestions for finding the logged on user from a windows service, but I am not sure if they work.
Your methods will run once, then the thread will exit. There is nothing to keep them running.
Try this:
thread1.IsBackground = true;
thread2.IsBackground = true;
public void start()
{
while(true)
{
// ... do stuff
Thread.Sleep(1000*60*5) // sleep for 5 minutes
}
}
public void TimerMeth()
{
while(true)
{
file write = new file();
write.write(RegKeys);
Thread.Sleep(30000);
}
}
As other posters have noted, you will also then need to ensure your main method doesn't exit. Making the application a windows service seems like a good way to solve this in your case.
You might also want to handle ThreadInterruptedException and ThreadAbortException on your threads.
And if you really want to get into the nitty gritty of threading, check out this Free C# Threading E-Book by Joe Albahari.
To keep the main thread alive, one of the simplest ways is to add the following line to the end of your main function:
Thread.Sleep(Timeout.Infinite);
The Thread will terminate when your ThreadStart function returns, which allows the main thread to continue after Join(). Since you are just setting up a timer to fire off, the method will return very quickly. You need to provide a lock of some sort to keep your application from exiting.
Also, it doesn't look like you need to use threads at all to do what you are trying. Instead, just use the Timer and provide the lock to keep your Main() from terminating.
It looks to me like all of your functions are completing? ie, they all "fall out the bottom". Once all the functions have run through there is nothing left to do and your app will close. You want to run a loop of some sort in main.
You will also need to take a look at your timer. I suspect it is currently being garbage collected. You create it in the scope of your function but that function is being left so there is no longer a reference to your timer and it will be collected. Your timer needs to be a root.