Here is my situation. I would like to make writing to the file system as efficient as possible in my application. The app is multi-threaded and each thread can possibly wr
Have a look at Asynchronous I/O. This will free up the cpu to continue with other tasks.
Combine with ReaderWriterLock as @Jack B Nimble mentioned
If by
writing to the file system as efficient as possible
you mean making the actual file I/O as fast as possible you are going to have a hard time speeding it up much, disk is just physically slower. Maybe SSD's?
What I would do is have separate worker thread(s) dedicated to the task of writing files out. When one of your other threads needs to write some data out, it should call a function to add the data to an ArrayList (or some other container/class). Inside this function, there should be a lock statement at the top to prevent more than one thread from executing simultaneously. After adding the reference to the ArrayList it returns and continues on with its chores. There are a couple of ways to handle the writing thread(s). Probably the simplest is to simply put it into an infinite loop with a sleep statement at the end so that it does not chew up your cpu(s). Another way is to use thread primitives and go into a wait state when there is no more data to be written out. This method implies that you would have to activate the thread with something like the ManualResetEvent.Set method.
There are many different ways to read in and write out files in .NET. I have written a benchmark program and give the results in my blog:
http://designingefficientsoftware.wordpress.com/2011/03/03/efficient-file-io-from-csharp
I would recommend using the Windows ReadFile and WriteFile methods if you need performance. Avoid any of the asynchronous methods since my benchmark results show that you get better performance with synchronous I/O methods.
You can use events for logger:
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace EventLogger
{
class Program
{
static void Main(string[] args)
{
// Event handler
LogData ld = new LogData();
// Logger
Logger lo = new Logger();
// Subscribe to event
ld.MyEvent += lo.OnMyEvent;
// Thread loop
int cnt = 1;
while(cnt < 5){
Thread t = new Thread(() => RunMe(cnt, ld));
t.Start();
cnt++;
}
Console.WriteLine("While end");
}
// Thread worker
public static void RunMe(int cnt, LogData ld){
int nr = 0;
while(true){
nr++;
// Add user and fire event
ld.AddToLog(new User(){Name = "Log to file Thread" + cnt + " Count " + nr, Email = "em@em.xx"});
Thread.Sleep(1);
}
}
}
class LogData
{
public delegate void MyEventHandler(object o, User u);
public event MyEventHandler MyEvent;
protected virtual void OnEvent(User u)
{
if(MyEvent != null){
MyEvent(this, u);
}
}
// Wywołaj
public void AddToLog(User u){
Console.WriteLine("Add to log.");
// Odpal event
OnEvent(u);
Console.WriteLine("Added.");
}
}
class User
{
public string Name = "";
public string Email = "";
}
class Logger
{
// Catch event
public void OnMyEvent(object o, User u){
try{
Console.WriteLine("Added to file log! " + u.Name + " " + u.Email);
File.AppendAllText(@"event.log", "Added to file log! " + u.Name + " " + u.Email+"\r\n");
}catch(Exception e){
Console.WriteLine("Error file log " + e);
}
}
}
}
Use Reader / Writer locks to access the file stream.
For those who prefer code, I am using following to do remote logging from web apps...
public static class LoggingExtensions
{
static ReaderWriterLock locker = new ReaderWriterLock();
public static void WriteDebug(this string text)
{
try
{
locker.AcquireWriterLock(int.MaxValue); //You might wanna change timeout value
System.IO.File.AppendAllLines(Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase).Replace("file:\\", ""), "debug.txt"), new[] { text });
}
finally
{
locker.ReleaseWriterLock();
}
}
}
Hope this saves you some time
While thread based locks can solve this, there is a manner which works across threads, but is probably best used when you have multiple processes writing to the end of a single file.
To get this behavior across processes (or threads too), specify that you want atomic append writes to the operating system when the OS file handles are created. This is done by specifying O_APPEND under Posix(Linux,Unix), and FILE_APPEND_DATA under Windows.
In C# you don't call the OS 'open', or 'CreateFile' system calls directly, but there are ways to get this result.
I asked how to do this under Windows a while ago, and got two good answers here: How can I do an atomic write/append in C#, or how do I get files opened with the FILE_APPEND_DATA flag?
Basically, you can use FileStream() or PInvoke, I would suggest FileStream() over PInvoke for obvious reasons.
You can use constructor arguments to FileStream() to specify asynchronous file I/O in addition to the FileSystemRights.AppendData flag, which should give you both async I/O and atomic append writes to a file.
Warning: Some OSes have limits on the maximum number of bytes that can be atomically written this way, and exceeding that threshold will remove the OS promise of atomicity.
Because of this last gotcha, I would recommend staying with lock() style contention management when trying to address your problem within a single process.