生產者消費者模式及簡單的運用場景


先考慮一個問題:服務端接受多個客戶端提交的視頻文件進行轉碼的操作,應該怎么設計?

由於轉碼比較花費時間,所以我們排除同步的想法。而轉碼需要用到的外部軟件(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.程序結束時,我們不能保證緩沖區數據是否全部處理完。改進:生產日志時,寫文件/數據庫,處理數據后,對處理過的數據進行標記,程序異常結束也沒問題,下次重啟先加載未處理數據,再一次展現單純加鎖的弊端。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM