简单的ThreadSafe日志类

时间:2013-02-11 22:17:48

标签: c# multithreading .net-4.0

  1. 我想知道我是否正确/有效地使用了这些组件。特别写入文件我不确定是否是线程安全的,虽然它没有在这个测试项目中抛出任何错误(即使没有睡眠)。我不知道这是不是因为广播公司已经同步,因此文件写入也随后同步......?

  2. 我不确定我是否可以使用普通的List<>或其他一些集合而不是BlockingCollection<>对于m_listeners。我可以在执行开始时干净地添加听众,但我想如果我想稍后删除它们,我就限制了自己。例如,我可能想要在未来更动态地添加/删除侦听器,例如在显示子表单时添加TextBox侦听器并在关闭时删除它。

  3. WindowsFormsApplication1包含:

    • Form1,注册OnLoad事件
    • button1,注册OnClick事件
    • button2,注册OnClick事件
    • textbox1,启用多行属性并展开以一次显示多行
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Collections.Concurrent;
    using System.IO;
    
    namespace WindowsFormsApplication1
    {
        public partial class Form1 : Form
        {
            //Fields
            private volatile bool cancel1 = false;
            private volatile bool cancel2 = false;
            private volatile bool cancel3 = false;
            private volatile bool cancel4 = false;
    
            public Form1()
            {
                InitializeComponent();
            }
    
            //Event Handlers
            private void button1_Click(object sender, EventArgs e)
            {
                //Automated Producers
                if (!cancel1)
                {
                    Task.Factory.StartNew(() =>
                    {
                        int count = 0;
                        while (!cancel1)
                        {
                            Log.Append("File", "log to file " + count++);
                            Thread.Sleep(1);
                        }
                        cancel1 = false;
                    });
                }
    
                if (!cancel2)
                {
                    Task.Factory.StartNew(() =>
                    {
                        int count = 0;
                        while (!cancel2)
                        {
                            Log.Append("GUI", "log to GUI " + count++);
                            Thread.Sleep(2);
                        }
                        cancel2 = false;
                    });
                }
    
                if (!cancel3)
                {
                    Task.Factory.StartNew(() =>
                    {
                        int count = 0;
                        while (!cancel3)
                        {
                            Log.Append("Error", "log to Error " + count++);
                            Thread.Sleep(3);
                        }
                        cancel3 = false;
                    });
                }
    
                if (!cancel4)
                {
                    Task.Factory.StartNew(() =>
                    {
                        int count = 0;
                        while (!cancel4)
                        {
                            Log.Append("", "log to console " + count++);
                            Thread.Sleep(4);
                        }
                        cancel4 = false;
                    });
                }
            }
    
            private void button2_Click(object sender, EventArgs e)
            {
                //Cancel Producers
                cancel1 = true;
                cancel2 = true;
                cancel3 = true;
                cancel4 = true;
            }
    
            private void Form1_Load(object sender, EventArgs e)
            {
                //Delete Old Files
                string LogFile = Directory.GetCurrentDirectory() + "\\Log.txt";
                if (File.Exists(LogFile))
                    File.Delete(LogFile);
    
                string VerboseLog = Directory.GetCurrentDirectory() + "\\VerboseLog.txt";
                if (File.Exists(VerboseLog))
                    File.Delete(VerboseLog);
    
                //Add Consumer Callback methods to Logger class
                //Append to File
                Log.RegisterWriter(
                    new Action<string, string>((tag, entry) =>
                    {
                        if (tag == "File")
                        {
                            using (TextWriter Stream = new StreamWriter(LogFile, true))
                            {
                                Stream.WriteLine(entry);
                            }
                        }
                    }));
    
                //Append to different file
                Log.RegisterWriter(
                    new Action<string, string>((tag, entry) =>
                    {
                        using (TextWriter Stream = new StreamWriter(VerboseLog, true))
                        {
                            Stream.WriteLine(DateTime.Now + ":\t" + entry);
                        }
                    }));
    
                //Append to Console
                Log.RegisterWriter(
                    new Action<string, string>((tag, entry) =>
                    {
                        Console.WriteLine(entry);
                    }));
    
                //Append to multiline textBox1
                Log.RegisterWriter(
                    new Action<string, string>((tag, entry) =>
                    {
                        if (tag == "GUI")
                        {
                            entry += "\r\n";
                            if (this.InvokeRequired)
                            {
                                this.BeginInvoke(new Action<string>(textBox1.AppendText), new object[] { entry });
                                return;
                            }
                            else
                            {
                                //Under the circumstances, this never occurs. An invoke is always required.
                                textBox1.AppendText(entry);
                                return;
                            }
                        }
                    }));
            }
        }
    
        public static class Log
        {
            //Fields
            private static BlockingCollection<Tuple<string, string>> m_logItems;
            private static BlockingCollection<Action<string, string>> m_listeners;
    
            //Methods
            public static void Append(string p_tag, string p_text)
            {
                //Add Log Entry
                m_logItems.Add(new Tuple<string, string>(p_tag, p_text));
            }
    
            public static void RegisterWriter(Action<string, string> p_callback)
            {
                //Add callback method to list
                m_listeners.Add(p_callback);
            }
    
            //Constructor
            static Log()
            {
                //Init Blocking Lists
                m_listeners = new BlockingCollection<Action<string, string>>();
                m_logItems = new BlockingCollection<Tuple<string, string>>();
    
                //Begin Log Entry Consumer Task
                Task.Factory.StartNew(() =>
                {
                    //Consume as Log Entries are added to the collection
                    foreach (var logentry in m_logItems.GetConsumingEnumerable())
                    {
                        //Broadcast to each listener
                        foreach (var callback in m_listeners)
                        {
                            callback(logentry.Item1, logentry.Item2);
                        }
                    }
                });
            }
        }
    }
    

    我不想使用第三方日志库(部分地更好地理解这些机制)并且只是想创建一种非常简单的方法来将项目从我的代码(任何线程)中的任何地方发布到各种输出(文件,文本框,控制台等)。

    实际应用程序中的登录频率远远低于此处所示(即它应该永远不会产生超过现实条件下可以消耗的频率)。

0 个答案:

没有答案