zoukankan      html  css  js  c++  java
  • 生产者消费者模式及简单的运用场景

    先考虑一个问题:服务端接受多个客户端提交的视频文件进行转码的操作,应该怎么设计?

    由于转码比较花费时间,所以我们排除同步的想法。而转码需要用到的外部软件(exe文件),不能同时被多个线程用到,所以我们排除为每一个客户端提交新建一个线程进行转码的想法。

    于是我们想到了静态加锁和队列。静态加锁有个缺点,稍后再提。当我们选择了队列,就选择了生产者消费者模式。

    其流程图:

    有流程图我们可以知道,生产者不关心数据什么时候被处理,消费者不关心数据什么时候产生,实现了解耦,也解决了阻塞。

    还有一个比较典型的例子便是日志的记录,多线程产生日志,但写日志由于文件独占,不能多线程来写,于是我们就可以把线程压入队列,由日志线程来读取队列数据,完成写日志的操作。下面是一个简单的实现:

    public class Log
    {
        private static ConcurrentQueue<LogMessage> msgs = new ConcurrentQueue<LogMessage>();
    
        public static void WriteLog(string msg)
        {
            msgs.Enqueue(new LogMessage(msg));
        }
    
        public static void Start()
        {
            Task.Factory.StartNew(() =>
            {
                while (true)
                {
                    while (msgs.TryDequeue(out LogMessage msg))
                    {
                        using (StreamWriter sw = new StreamWriter(msg.LogFile, true))
                        {
                            sw.WriteLine(msg.Message);
                        }
                    }
                    Thread.Sleep(1000);
                }
            });
        }
    }

    这个是写日志的类

    class LogMessage
    {
        public string Message { get; set; }
        public string LogFile { get; set; }
    
        public LogMessage(string msg)
        {
            this.Message = $"{DateTime.Now.ToString("HH:mm:ss ")} {msg}";
            string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Log", DateTime.Now.ToString("yyyy-MM"));
            if (!Directory.Exists(path))
                Directory.CreateDirectory(path);
            this.LogFile = Path.Combine(path, DateTime.Now.ToString("dd") + ".log");
        }
    }

    这个是日志结构。包括产生日志的时间和写日志的日志文件。可以实现23:59产生的日志写到当天的文件夹中。

    日志工具类的调用也非常简单,直接调用静态方法WriteLog就行。

    回到开头所说加锁的弊端:线程排队并不是在队列中,没有先后顺序的保证,牵扯到严格顺序时就会有问题,比如写日志,socket数据接受等。

    模式的应用场景:处理数据比较消耗时间,线程独占,生产数据不需要即时的反馈等。

    例子的不足:

    1.省略掉了缓冲区,使得生产者和消费者并不是完全解绑。改进:用一个独立的数据结构来放置数据,可以是缓存、文件、数据库,实现仅依赖于数据格式的解绑。

    2.程序结束时,我们不能保证缓冲区数据是否全部处理完。改进:生产日志时,写文件/数据库,处理数据后,对处理过的数据进行标记,程序异常结束也没问题,下次重启先加载未处理数据,再一次展现单纯加锁的弊端。

  • 相关阅读:
    Dom 动态添加元素节点总结
    SQLserver 获取当前时间
    Var的用法解析
    JS 转换HTML转义符
    20210602---为了养老,全力以赴,珍惜每一秒钟。决心不够大,不够担心未来,现在虽然挣得少,但是有吃有喝,满足了。
    20210601——今天开始狠狠奖励自己,而且是必须玩的这种。做事投入你就会快乐。
    20210531兴趣
    20210527学习笔记--没成功的唯一原因是,想得和说的太多 做的太少。
    20210526--今年还有半年,抓紧一切时间学习
    20210524学习笔记---从记日记开始已经有3个月了,浪费时间的痕迹渐渐清醒
  • 原文地址:https://www.cnblogs.com/blogXy/p/7755946.html
Copyright © 2011-2022 走看看