Use BlockingCollection to implement producer and consumer queues, and write text,
Recently, we have developed several small projects that need to write the results to the txt file and split the files by time. Due to high efficiency requirements, we use the producer and consumer models to write the text, in the thread, you only need to add a queue and return immediately, instead of waiting for the time to write the file.
Thanks @ cnc for its correction. I did not assign a value for the date of the new day in the Task. Thanks again.
public class WriteItem : IDisposable { public string Filename { get; private set; } public Encoding Encode { get; } public bool Append { get; } public bool TimeName { get; } private StreamWriter _writer; private readonly BlockingCollection<string> _blocking = new BlockingCollection<string>(); public void Write(string msg) { _blocking.Add(msg); } public void WriteLine(string msg) { Write(msg + Environment.NewLine); } public WriteItem(string filename, Encoding encode, bool append = true, bool timeName = false) { Filename = filename; Encode = encode; Append = append; TimeName = timeName; if (timeName && string.IsNullOrEmpty(Path.GetExtension(filename))) { this.Filename = Path.Combine(this.Filename, DateTime.Now.ToString("yyyy-MM-dd") + ".txt"); } var dir = Path.GetDirectoryName(this.Filename); Directory.CreateDirectory(dir ?? throw new InvalidOperationException()); _writer = new StreamWriter(this.Filename, this.Append, this.Encode) { AutoFlush = true }; Task.Factory.StartNew(() => { foreach (var s in _blocking.GetConsumingEnumerable()) { if (TimeName) { var fileNameWithoutExtension = Path.GetFileNameWithoutExtension(this.Filename); var nowDay = DateTime.Now.ToString("yyyy-MM-dd").ToString(); if (fileNameWithoutExtension != nowDay) { _writer.Dispose(); this.Filename = Path.Combine(Path.GetDirectoryName(this.Filename), nowDay + ".txt"); _writer = new StreamWriter(this.Filename, this.Append, this.Encode) { AutoFlush = true }; } } _writer.Write(s); } }, TaskCreationOptions.LongRunning); } public void Dispose() { _writer?.Dispose(); _blocking?.Dispose(); } }
Then a dictionary is written for maintenance:
public class FileWriteQueue { private static readonly Dictionary<string, WriteItem> Dictionary = new Dictionary<string, WriteItem>(); public static void AddOrUpdate(string key, WriteItem item) { if (Dictionary.ContainsKey(key)) { Dictionary[key].Dispose(); Dictionary[key] = item; } else { Dictionary.Add(key, item); } } public static WriteItem Get(string key) { return Dictionary[key]; } }
In actual use, add WirteItem and set the output directory:
FileWriteQueue. addOrUpdate ("success", new WriteItem (Path. combine ("result", "successful"), Encoding. default, true, true); FileWriteQueue. addOrUpdate ("error", new WriteItem (Path. combine ("result", "failed"), Encoding. default, true, true); for (int I = 0; I <1000; I ++) {FileWriteQueue. get ("success "). writeLine (I. toString ());}