Please bear with me for this long question.
Scenario:
I have a FileSystemWatcher
on the root-directory which is watching for LastWrite
change on control-file each inside a control-directory. Looks something like this:-
root-directory (
FileSystemWatcher
instance is watching this root directory)|
|-----control-directory_0 \ control-file
\ data-file
|-----control-directory_1 \ control-file
\ data-file
|-----control-directory_2 \ control-file
\ data-file
|-----control-directory_3 \ control-file
\ data-file
.........more similar structure (but only one root-directory)
Sorry for my creative drawing.
The problem I was facing, there can be many control-directories inside the root-directory (approx 200-500) and along with control-directory's control-file, I have a data-file in it (in each of them) where continuous writes are happening.
I was watching for NotifyFilter.LastWrite
and Filter = control-file
.
InternalBufferSize
is set to 4KB (please don't ask to increase this, I am not allowed to modify it and event to make it 8KB (default one) due to requirements and I can't use FileSystemWatcher
for each control-file as it will take lot of precious non-paged memory).
My EventHandler
looks like this
private void OnChange(Object sender, FileSystemEventArgs eventArgs)
{
//result = CPU bound work here
//evaluating result
}
Currently, I am handling 300 control-files. Due to lots of writes happening on data-file and lots of requests coming to control-file my buffer is overflowing quite regularly.
Then I thought of an idea. (async
and await
to the rescue with Task
battle tank)
async private void OnChange(Object sender, FileSystemEventArgs eventArgs)
{
var result = await Task.Run(() => Work.CPUboundWork());
//evaluating result
}
It was working fine and I was able to handle lots of requests (even tested with 1000 events with only 4KB buffer).
Now comes the interesting part. How am I testing it?
There are two entities involved, one, writing to the control or data-file and second, handling the events generated by the writes to the control-file. So I went up and created a console app, which does both of the things.
static void Main(string[] args)
{
int controlFileCount = Convert.ToInt32(args[0]);
string basePath = args[1];
//CreateFolderStructure(basePath, controlFileCount); for first run.
FileSystemWatcher baseDirectoryWatcher = new FileSystemWatcher();
SetupFileSystemWatcher(baseDirectoryWatcher, basePath);
WriteToControlFilesParallely(basePath, controlFileCount);
Console.Read();
}
public void SetupFileSystemWatcher(FileSystemWatcher baseDirectoryWatcher, string path)
{
//setting properties of the watcher.
baseDirectoryWatcher.NotifyFilter = NotifyFilter.LastWrite;
baseDirectoryWatcher.Filter = "control-file";
baseDirectoryWatcher.InternalBufferSize = 4096;
baseDirectoryWatcher.Path = path;
baseDirectoryWatcher.IncludeSubdirectories = true;
baseDirectoryWatcher.EnableRaisingEvents = true;
}
public void WriteToControlFilesParallely(string basePath, int controlFileCount)
{
Parallel.For(0, controlFileCount, (i) =>
{
string filePath = Helper.GetFilePath(basePath, i);
Helper.WriteData(filePath, "data");
});
}
Next, I tested with two console applications:-
First, Responsible of writing data to various control files parallelly - (Writer_App).
Second, Responsible for handling events - (Event_Handling_App).
NOTE: There's no code change in the core but instead of writing and handling the events in the same console app now I am writing from one and handling form other.
So I separated the two entities from my old console app (starting a simulation to the actual environment). Now I started testing by running first Event_Handling_App and then write to the control-file by running Writer_App and now the things have become strange and my application is facing InternalBufferOverflow
exception for the same number of control-files and same number of writes when I didn't have the async
and await
as in my first implementation.
I again tested with one console app (which is having both the responsibilities) and it was working fine.
So, why two console applications make the magic of
async
andawait
(along withTask
) disappear while one console application is working great?Is it something related to Inter Process Communication?
I am wondering if it will work in production because I will be using handler code in a Web application and some other process can write these files over the network.
Finally, I was able to figure out this, when I was using single console app I was running its exe (through cmd) but when I was trying with two console app I was running the app inside visual studio (in release mode) so due to this (with load of visual studio) I was getting the exception and I was blaming async
and await
.
So, Always run your application from exe if you are dealing with code which is tight.