Please excuse me if this is redundant, however all the questions related to this seem to point in different directions, also I am new to multithreaded programming.
I have a FileSystemWatcher class in my code, which watches the created event. It looks like a created event of file system watcher starts it's own thread. So sometimes the calling thread continues it's work before the work initiated in called thread of FileSystemWatcher created event finishes. I don't want this. My workflow needs to be single-threaded, so what I want to achieve is wait for for created event to finish it's work before the calling thread gets an opportunity to resume.
pesudo code:
main() {
FileSystemWatcher fsw = new FileSystemWatcher()
fsw.Path = ini.location;
fsw.Created += new FileSystemEventHandler(OnFileCreation);
fsw.EnableRaisingEvents = true;
main_engine.processDataToFile();
main_engine.processCreatedFile();
}
void OnFileCreation(object sender, FileSystemEventArgs e) {
// do some file processing
// takes time based on size of file and whether file is locked or not etc.
}
void processDataToFile() {
// do some data processing on received data and output to a file.
}
void processCreatedFile() {
// do not want this method to be called, unless OnFileCreation() finish it's work.
}
The reason choose to use FileSystemWatcher was because sometimes files are directly placed for processing instead of main_engine getting the data first and it works on multiple locations, so did not want to roll out a homegrown solution when FileSystemWatcher was available.
If the event fires in the separate thread you cant make it single-threaded. because this is not your code. end of story.
however it is quite simple to wait on:
...
me.WaitOne();
main_engine.processCreatedFile();
}
...
void OnFileCreation(object sender, FileSystemEventArgs e) {
// do some file processing
// takes time based on size of file and whether file is locked or not etc.
...
me.Set();
}
ManualResetEventSlim me = new ManualResetEventSlim(false);
Related
First of all my Main is STAThread and i am not able to change this without facing problems with the rest of my code.
So, I am currently using Rapi2 To pull and push files between my Pda and Computer. Now since there is quite a bit of number crunching i would like to do this on a separate thread. First wat i do is create an RemoteDeviceManager and then make an Event Handler for when a device connects.
public void Initialize()
{
_deviceManager = new RemoteDeviceManager();
_deviceManager.DeviceConnected += DeviceConnected;
}
As you can see when my device connects it triggers DeviceConnected.
This is the class that i end up pulling and pushing a database and do some number work.
private void DeviceConnected(object sender, RemoteDeviceConnectEventArgs e)
{
if (e.Device == null) return;
... (unimportant code)
}
Now the problem here is that i would want to run the code inside DeviceConnected in a new thread but i am unable to access e inside the new thread since it was initialized outside that thread
So now wat i tried was make a new thread before calling Initialize.
public Watcher()
{
_dataThread = new Thread(Initialize);
_dataThread.IsBackground = true;
_dataThread.Name = "Data Thread";
_dataThread.SetApartmentState(ApartmentState.MTA);
_dataThread.Start();
}
But the thread dies and thus never fires my event handler.
I tried many different ways to make it work or keep my thread alive but without any success. I hope someone here is able to give me some hints.
I have a very simple console app with the entirety of the high level code shown below:
class Program
{
static Timer timer = new Timer(TimeSpan.FromMinutes(5).TotalMilliseconds);
static void Main(string[] args)
{
timer.Elapsed += Timer_Elapsed;
timer.Start();
Timer_Elapsed(null, null);
Console.ReadKey();
}
private static void Timer_Elapsed(object sender, ElapsedEventArgs e)
{
timer.Stop();
var syncSftp = new Tasks.WindowsServiceTasks.SFTPSync();
Common.Logging.Logger = new Common.Logging.TextFileAndConsoleLogger();
syncSftp.Execute();
timer.Start();
}
}
There seems to be a strange occurrence where after about 2 days the timer seems to stop triggering the Elapsed event.
The quick rundown on this app: It monitors sever folders and sends files out via SFTP. It also goes out and grabs files via SFTP and stores them on local folders. I have logging on every server connection, file move, copy, etc.
It seems that after 2 days the console app stops showing any logging at all. This is what leads me to think the Elapsed event stops firing. Not only that but we stop getting files from inbound SFTP and stop sending outbounds. There are no errors whatsoever. The only thing I can think of here is that somehow the console app may be trying to move or copy a file that is open somewhere else.
What would cause a Timer.Elapsed event to stop firing in simple IO processes?
I'm thinking maybe there is a catch ya here somewhere such as the program running out of memory or something; however, I do not store any incremented variables such as lists or anything of the sort.
In order to avoid polluting the post with extra code that may not be needed I have added the SFTPSync class to a paste bin here: http://pastebin.com/VfWbPpWt
I creating an windows service which will from time to time check if a certain exists and if it does, then reads from it, sends data to a server and move a file to another folder. A file's size is about 1-3 Mb.
I think I will use System.Threading.Timer here to check if a file exists. What do you think of it?
And another question. If a file is being copied then my app must not read from it. It should wait until copying is done. Only after that it must read from it and does other activities.
So the questions:
1) Is that a right decision to use System.Threading.Timer?
2) How do I check a file is being copied and wait until it's done?
3) Must I use multi-threading?
I think I will use System.Threading.Timer here to check if a file exists. What do you think of it?
I think you might take a look at the FileSystemWatcher class which will notify you when the file is created and raise an event instead of you using a Timer that will continuously poll for the existence of the file.
Timer is very much costly . You can use FileSystemWatcher Which Listens to the file system change notifications and raises events when a directory, or file in a directory, changes.
// Create a new FileSystemWatcher and set its properties.
FileSystemWatcher watcher = new FileSystemWatcher();
watcher.Path = /*path*/
/* Watch for changes in LastAccess and LastWrite times, and
the renaming of files or directories. */
watcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite
| NotifyFilters.FileName | NotifyFilters.DirectoryName;
// Only watch text files.
watcher.Filter = "*.txt";
// Add event handlers.
watcher.Changed += new FileSystemEventHandler(OnChanged);
watcher.Created += new FileSystemEventHandler(OnChanged);
watcher.Deleted += new FileSystemEventHandler(OnChanged);
watcher.Renamed += new RenamedEventHandler(OnRenamed);
// Begin watching.
watcher.EnableRaisingEvents = true;
Then this would be the OnChanged method:
//This method is called when a file is created, changed, or deleted.
private static void OnChanged(object source, FileSystemEventArgs e)
{
//Show that a file has been created, changed, or deleted.
WatcherChangeTypes wct = e.ChangeType;
Console.WriteLine("File {0} {1}", e.FullPath, wct.ToString());
}
Reference: http://devproconnections.com/net-framework/how-build-folder-watcher-service-c
The usual approach I've used is to monitor the folder/s with FileSystemWatcher, (or the relevant API's if not .NET managed), and try to ensure that the only operations performed on the source/target folders are move/rename between folders on the same physical drive, and delete. If you want to add a file, open/write/flush/close it ito a temp folder on the target filesystem drive and only then move/rename it to the folder being watched. It is vital that the temp folder is on the same physical drive as the target folder so that it can be move/renamed without a data copy.
This works well on non-managed systems, not tried it on C#, but don't see any reason for to not to work OK.
Other solutions involving continual polling and/or checking file sizes are just inconvenient, inflexible, wasteful, messy and latency-ridden at best.
Multithreading - probably yes on any remote filesystem. Network file calls tend to have very long timeouts on unrechability etc. and so block the caller for what seems like forever before issuing an error/exception. If you want to get anything else done, you should probably thread off the lot unless your users can tolerate 'hourglass apps', with windows becoming unresponsive, disappearing to back, getting greyed-out and the OS offering to close them.
Oh, and another thing - best to go on a purge when starting up. Stuff can go wrong at any time, so clean any lingering rubbish from temp folders etc. when running up.
I would not use the FileSystemWatcher it's too flaky
FileSystemWatcher does not work on files created from windows service
I would use the timer set to a reasonable tick period and you should be ok.
Some sample code
Timer_TicK()
{
// remove tick event handler
// do your processing
// add back tick event handler
}
This will keep multiple tick events from happening if you have a rather large amount of processing to do. I wouldn't multi-thread until you find you need to use it due to performance issues.
In C# if you try and read a file while it's being copied by the file system you will get an exception. You will need to check for that exception or check the properties of the file (size, etc) to know when it's done.
Full timer example, where I start new threads each time the interval happens (you don't have to, but it is a good practice), which goes and checks if the file is there and reads it and deletes it if it is:
using System;
using System.ServiceProcess;
using System.Threading;
using System.Timers;
using System.IO;
namespace MyNamespace
{
public partial class Service1: ServiceBase
{
Thread syncThread = null;
System.Timers.Timer timer1;
string filePath = #"C:\myfile.txt";
int interval = 60000; // 1 min -- adjust as necessary
public Service1()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
timer1 = new System.Timers.Timer();
timer1.Interval = interval;
timer1.Enabled = true;
timer1.Elapsed += new ElapsedEventHandler(timer1_Elapsed);
timer1.Start();
}
protected override void OnStop()
{
syncThread.Abort();
timer1.Stop();
}
protected void timer1_Elapsed(object sender, ElapsedEventArgs e)
{
syncThread = new Thread(new ThreadStart(doThread));
syncThread.Start();
}
protected void doThread()
{
// whatever you put here, it will
// run for each timer interval that elapses
// in a separate thread, and each thread will
// end when the processing in this function ends
string fileContent = String.Empty;
if (File.Exists(filePath)
{
FileStream fs = new FileStream(filePath, FileMode.Open);
StreamReader sr = new StreamReader(fs);
fileContent = sr.ReadToEnd();
sr.Close();
fs.Close();
}
if (fileContent != String.Empty)
{
// File was present... process the content...
// Then I do this...
File.Delete();
}
}
}
}
I've ran it with no problems. I prefer to start new threads with each time interval so it doesn't cause issues if the previous run hasn't finished yet. Going this route, you have that control, can decide what your interval is, and your process is not always going on - just the service. The other route, with FileSystemWatcher, it is always watching and running whenever it can and you cannot adjust your time interval, like you can with a timer, to keep down the number of events/processes going on, such as when/if the file is created, then quickly modified & saved.
Only downside I see is having to check the file attributes yourself, but those are easy enough checks to make. If you do File.Delete() after processing it like I do, you only have File.Exists(filePath) to do, in order to find out if the file has been re-created. If you have to check for modification, you merely check DateTime lastModifed = File.GetLastWriteTime(filePath) (see http://www.csharp-examples.net/file-creation-modification-time/ ) against the current time + your interval (which would be DateTime lastRun = DateTime.Now.AddMilliseconds(interval)). If lastRun > lastModified, you would have to process it.
Part of my program uses an event handler for the receive data of my serial port. The idea is when data is received that the text received is then added to the textbox (rx). I did not used to have this problem but something has changed and I can't figure out what. So now I am re-examining the way this is handled.
During the form load of my winform the last thing I do is
if (!serialPort1.IsOpen)
{
serialPort1.Open();
serialPort1.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
}
Then I have the event handler
private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
string indata1 = serialPort1.ReadExisting();
// rx.Text = " "; accidentally posted this. it was from trial and error.
rx.AppendText(Environment.NewLine + indata1);
}
When I run the program it stops at the rx.AppendText(Environment.NewLine + indata1); and gives the error
invalidoperationexception was unhandled: Control "accessed from a
thread other than the thread it was created on.
From what I have been able to read suggests that I need to use invoke or BeginInvoke.
I have never had problems appending the text before so now I can't understand why it's a problem. Also from what I have been reading on invoking i just don't understand it.
Can someone help me understand how to use the invoke instance for my situation? or perhaps show me another way of appending the text box?
Usually the exception you're seeing occurs when you run in debug mode, and if you run your application in release mode, you're unlikely to see the exception.
However, it is best to use invoke, as you have read. Something like this:
private delegate void RefreshTextBox();
private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e) {
//this event is raised in an event separate from UI thread,
//so InvokeRequired must be checked and Invoke called to update UI controls.
if (this.InvokeRequired) {
RefreshTextBox d = new RefreshTextBox(RefreshTextBoxResults);
Invoke(d);
} else {
RefreshTextBoxResults();
}
}
private void RefreshTextBoxResults() {
string indata1 = serialPort1.ReadExisting();
rx.Text = " ";
rx.AppendText(Environment.NewLine + indata1);
}
The first time you see this invoke stuff, it's nearly impossible to follow, but take a close look and give it some time and it will make sense. Promise. :)
Updates in GUI applications should only be done on the GUI thread. Another thread attempting to update GUI components directly will result in either the error you described or in seemingly random behavior.
The role of Invoke & friends is to enable a secondary thread to safely forward GUI updates to the GUI thread, which will then process them from a queue.
In your case (assuming WinForms here):
rx.BeginInvoke(
(Action)(() =>
{
rx.AppendText(Environment.NewLine + indata1);
}));
BeginInvoke is asynchronous, so the thread calling it will not wait for the actual updates to be processed before moving on, while Invoke is synchronous.
I'm making a program that controls a game server. One of the functions I'm making, is a live server logfile monitor.
There is a logfile (a simple textfile) that gets updated by the server as it runs.
How do I continuously check the logfile and output it's content in a RichTextBox?
I did this simple function just try and get the content of the log. It will of course just get the text row by row and output it to my textbox. Also it will lock the program for as long as the loop runs, so I know it's useless.
public void ReadLog()
{
using (StreamReader reader = new StreamReader("server.log"))
{
String line;
// Read and display lines from the file until the end of the file is reached.
while ((line = reader.ReadLine()) != null)
{
monitorTextBox.AppendText(line + "\n");
CursorDown();
}
}
}
But how would you go about solving the live monitoring as simple as possible?
*** EDIT ***
I'm using Prescots solution. great stuff.
At the moment I'm using a sstreamreader to put the text from the file to my textbox. I ran into the problem is that, whenever I tried to access any of the gui controls in my event handler the program just stopped with no error or warnings.
I found out that it has to do with threading. I solved that like this:
private void OnChanged(object source, FileSystemEventArgs e)
{
if (monitorTextField.InvokeRequired)
{
monitorTextField.Invoke((MethodInvoker)delegate { OnChanged(source, e); });
}
else
{
StreamReader reader = new StreamReader("file.txt");
monitorTextField.Text = "";
monitorTextField.Text = reader.ReadToEnd();
reader.Close();
CursorDown();
}
}
Now my only problem is that the file.txt is used by the server so I can't access it, since it's "being used by another process". I can't control that process, so maybe I'm out of luck.
But the file can be opened in notepad while the server is running, so somehow it must be possible. Perhaps I can do a temp copy of the file when it updates and read the copy. I don't know.
Check out the System.IO.FileSystemWatcher class:
public static Watch()
{
var watch = new FileSystemWatcher();
watch.Path = #"D:\tmp";
watch.Filter = "file.txt";
watch.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite; //more options
watch.Changed += new FileSystemEventHandler(OnChanged);
watch.EnableRaisingEvents = true;
}
/// Functions:
private static void OnChanged(object source, FileSystemEventArgs e)
{
if(e.FullPath == #"D:\tmp\file.txt")
{
// do stuff
}
}
Edit: if you know some details about the file, you could handle the most efficent way to get the last line. For example, maybe when you read the file, you can wipe out what you've read, so next time it's updated, you just grab whatever is there and output. Perhaps you know one line is added at a time, then your code can immediately jump to the last line of the file. Etc.
Although the FileSystemWatcher is the most simple solution I have found it to be unreliable in reality.. often a file can be updated with new contents but the FileSystemWatcher does not fire an event until seconds later and often never.
The only reliable way I have found to approach this is to check for changes to the file on a regular basis using a System.Timers.Timer object and checking the file size.
I have written a small class that demonstrates this available here:
https://gist.github.com/ant-fx/989dd86a1ace38a9ac58
Example Usage
var monitor = new LogFileMonitor("c:\temp\app.log", "\r\n");
monitor.OnLine += (s, e) =>
{
// WARNING.. this will be a different thread...
Console.WriteLine(e.Line);
};
monitor.Start();
The only real disadvantage here (apart from a slight performance delay caused by file size checking) is that because it uses a System.Timers.Timer the callback comes from a different thread.
If you are using a Windows Forms or WPF app you could easily modify the class to accept a SynchronizingObject which would ensure the event handler events are called from the same thread.
As #Prescott suggested, use a FileSystemWatcher. And make sure, you open the file with the appropriate FileShare mode (FileShare.ReadWrite seems to be appropriate), since the file might still be opened by the server. If you try to open the file exclusively while it is still used by another process, the open operation will fail.
Also in order to gain a bit of performance, you could remember the last position up to which you already have read the file and only read the new parts.
Use this answer on another post c# continuously read file.
This one is quite efficient, and it checks once per second if the file size has changed.
You can either run it on another thread (or convert to async code), but in any case you would need to marshall the text back to the main thread to append to the textbox.
Try adding a Timer and have the Timer.Tick set to an Interval of 1 second. On Timer.Tick you run the function.
private void myTimer_Tick(object sender, EventArgs e)
{
ReadLog();
}