Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multi-threaded file write enqueueing

So I have a static class that is supposed to be used as a log file manager, capable of adding "messages" (strings) to a Queue object, and that will push messages out to a file. Trouble is, many different threads should be enqueueing, and that the writer needs to be async as well. Currently when I insert into the queue, I'm also checking to see if the writer is writing (bool check), if it's not, i set the bool and then start the writing, but I'm getting intermittent IO exceptions about file access, and then wierd writing behavior sometimes.

Someone want to give me a hand on this?

like image 335
Firoso Avatar asked Nov 22 '25 21:11

Firoso


1 Answers

If you don't want to restructure your code dramatically like I suggested in my other answer, you could try this, which assumes your LogManager class has:

  • a static thread-safe queue, _SynchronizedQueue
  • a static object to lock on when writing, _WriteLock

and these methods:

public static void Log(string message) {
    LogManager._SynchronizedQueue.Enqueue(message);
    ThreadPool.QueueUserWorkItem(LogManager.Write(null));
}

// QueueUserWorkItem accepts a WaitCallback that requires an object parameter
private static void Write(object data) {
    // This ensures only one thread can write at a time, but it's dangerous
    lock(LogManager._WriteLock) {
        string message = (string)LogManager._SynchronizedQueue.Dequeue();
        if (message != null) {
            // Your file writing logic here
        }
    }
}

There's only one problem: the lock statement in the Write method above will guarantee only one thread can write at a time, but this is dangerous. A lot can go wrong when trying to write to a file, and you don't want to hold onto (block) thread pool threads indefinitely. Therefore, you need to use a synchronization object that lets you specify a timeout, such as a Monitor, and rewrite your Write method like this:

private static void Write() {
    if (!Monitor.TryEnter(LogManager._WriteLock, 2000)) {
       // Do whatever you want when you can't get a lock in time
    } else {
      try {
         string message = (string)LogManager._SynchronizedQueue.Dequeue();
         if (message != null) {
             // Your file writing logic here
         }
      }
      finally {
        Monitor.Exit(LogManager._WriteLock);
      }
    }
}
like image 95
Jeff Sternal Avatar answered Nov 25 '25 11:11

Jeff Sternal