首页 新闻 会员 周边 捐助

关于.net线程安全问题

0
悬赏园豆:50 [已解决问题] 解决于 2016-03-10 12:31

 public class Logger
    {
        
        private Queue<Action> queue;
        private ManualResetEvent manual;

       
        private Thread logThread;
        /// <summary>
        /// 指定存储文件的路径
        /// </summary>
        public static string Path { get; set; }

        //私有构造函数,初始化相关对象 使用单例模式
        private Logger()
        {
            queue = new Queue<Action>();
            manual = new ManualResetEvent(false);
            logThread = new Thread(Process);
            logThread.IsBackground = true;
            logThread.Start();
        }
        private readonly static Logger logger = new Logger();
        private static Logger GetInstance()
        {
            return logger;
        }
        //不断处理队列中的任务
        private void Process()
        {
            while (true)
            {
                
                manual.WaitOne();

             
                manual.Reset();

               
                Thread.Sleep(100);

              
                Queue<Action> copy;
                lock (queue)
                {
                    copy = new Queue<Action>(queue);
                    queue.Clear();
                    foreach (var action in copy)
                        action();
                }
            }
        }
        private void ExcuteAction(string log)
        {
            string date = DateTime.Now.ToString();
            string path;
            if (!string.IsNullOrEmpty(Path))
                path = Path + "log.txt";
            else
                path = "log.txt";
            lock(queue)
            {
             
                queue.Enqueue(() => File.AppendAllText(path, log+" : "+date + Environment.NewLine));
            }
            
            manual.Set();
        }

        
        /// <summary>
        /// 将数据写入文件中
        /// </summary>
        /// <param name="log">要写入的数据</param>
        public static void WriteLog(string log)
        {
            // WriteLog 方法只是向队列中添加任务,执行时间极短,所以使用Task.Run。
            Task.Run(() =>
            {
                GetInstance().ExcuteAction(log);
            });
        }

}

 两条线程读写同一个对象,两条线程都lock这个对象
 用ManualResetEvent来控制读数据的线程(Thread(Process))
 当写数据的线程(Task.Run)有数据写入时开启读线程工作
 这样有可能控制并发,但测试几次,发现几次数据的顺序有误,但没有漏
主线程和新线程调用第三次后顺序没错 
开Task线程调用顺序完全相反,开始两次也会有些乱!!!!!!!!!!!!

问题补充:

调用代码

class Program
    {
        static void Main(string[] args)
        {
           

            //Task.Run(() => TestLogger());

            //TestLogger();

            new Thread(TestLogger).Start();
           
            Console.ReadKey();
        }
        static void TestLogger()
        {
            for (int i = 0; i < 50; i++)
            {
                Logger.WriteLog(i.ToString());
            }
        }
    }

随机哥丶的主页 随机哥丶 | 初学一级 | 园豆:159
提问于:2016-03-10 01:03
< >
分享
最佳答案
1

从队列中取元素的时候不要foreach要出队.放进去的也时候,操作private Queue<Action> queue;对象就好.不要想太多.

人家队列类都给你做好这些东西了.你不用怪谁.

收获园豆:20
吴瑞祥 | 高人七级 |园豆:29449 | 2016-03-10 09:20

lock (queue)
                {
                    while (queue.Count > 0)
                    {
                        var action = queue.Dequeue();
                        action();
                    }
                }

您的意思是改成这样吧,谢谢指点

不过还是有乱序的问题

随机哥丶 | 园豆:159 (初学一级) | 2016-03-10 12:00
其他回答(2)
0

把WriteLog中的线程去了

收获园豆:20
jello chen | 园豆:7336 (大侠五级) | 2016-03-10 09:15

谢谢指点

支持(0) 反对(0) 随机哥丶 | 园豆:159 (初学一级) | 2016-03-10 12:03

是的,应该让客户端开线程做写入

支持(0) 反对(0) 随机哥丶 | 园豆:159 (初学一级) | 2016-03-10 12:19
0

Queue换成ConcurrentQueue,其他所有互斥量全部拿掉,就当做单线程处理即可。

另while(true)这种做法不好,相当于你就只考虑开始不考虑结束,如果中途需要停止最好是通过通知的方式传过来。

收获园豆:10
Daniel Cai | 园豆:10424 (专家六级) | 2016-03-10 09:52

谢谢指点,我也不喜欢while(true)我就是想试试线程安全和并发,我是业余自学.net的,文化有限,初中毕业,现在人也老了,估计是没希望从事这行了。开始学的还蛮好的,后面越来越觉得要学的太多

支持(1) 反对(0) 随机哥丶 | 园豆:159 (初学一级) | 2016-03-10 12:09

@随机哥丶: .net的学习曲线较java而言平滑一些,当然随着了解的深入涉及的东西就会越多,这个是没有办法的。关于多线程,建议先不要看task和并行,直接学thread及threadpool,几种信号量。练手也不要以有外部依赖的东西着手(比如你读写文件,这个实际会使场景变的更复杂),选用普通的cpu密集型计算更简单而且也容易验证些(比如数字的累加)。

支持(1) 反对(0) Daniel Cai | 园豆:10424 (专家六级) | 2016-03-10 17:39
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册