Write to a file from multiple threads asynchronously c#

后端 未结 7 1559
忘掉有多难
忘掉有多难 2020-12-02 14:04

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

相关标签:
7条回答
  • 2020-12-02 14:44

    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?

    0 讨论(0)
  • 2020-12-02 14:49

    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.

    0 讨论(0)
  • 2020-12-02 14:54

    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);
                }
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-02 14:59

    Use Reader / Writer locks to access the file stream.

    0 讨论(0)
  • 2020-12-02 15:02

    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

    0 讨论(0)
  • 2020-12-02 15:03

    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.

    0 讨论(0)
提交回复
热议问题