簡體   English   中英

如何處理TPL數據流中的異常-生產者/消費者

[英]How to handle Exceptions in a TPL Dataflow - Producer/Consumer

我有以下測試應用程序可以模擬我的應用程序場景:

class Program
{
    static void Main()
    {
        Console.WriteLine("***Press R = record to new file, S = Stop Recording, E = Exit");
        var timer = new Timer(Callback, null, 0, 1000);
        while(!_Close) HandleInput(Console.ReadLine());
        Console.WriteLine("Finished");
    }

    private static bool _Close;

    private static void HandleInput(string input)
    {
        switch (input.ToLower())
        {
            case "r": CreateWriter();
                break;
            case "s": Console.WriteLine("File Closed: {0}", _FileWriter.Name); 
                _FileWriter.Dispose();
                _FileWriter = null;
                break;
            case "e":
                _Close = true;
                break;
        }
    }

    private static void CreateWriter()
    {
        if (_FileWriter != null)
            _FileWriter.Dispose();
        string filename = Path.Combine("C:\\", string.Format("{0:yyyy_MM_dd HH_mm_ss}.txt",DateTime.Now));
        _FileWriter = new AsyncFileWriter(filename);
        Console.WriteLine("New File Created: {0}", filename);
    }

    private static void Callback(object state)
    {
        if (_FileWriter != null)
            _FileWriter.Produce(MakeData());
    }

    private static byte[] MakeData()
    {
        string data = string.Empty;
        for (int i = 0; i < 50; i++)
        {
            data += string.Format("{0:yyyy-MM-dd HH:mm:ss.fff}{1}", DateTime.Now, Environment.NewLine);
        }
        return Encoding.UTF8.GetBytes(data);
    }

    private static AsyncFileWriter _FileWriter;
}

它使用以下類作為Consumer(/ producer):

public class AsyncFileWriter : IDisposable
{
    private readonly FileStream _Filewriter;
    private readonly Task _WriteTask;
    private readonly BufferBlock<byte[]> _BufferBlock;

    public AsyncFileWriter(string filename)
    {
        _Filewriter = new FileStream(filename, FileMode.CreateNew, FileAccess.Write, FileShare.None);
        _BufferBlock = new BufferBlock<byte[]>();
        _WriteTask = WriteToFile();
    }

    public void Produce(byte[] data)
    {
        _BufferBlock.Post(data);
    }

    public long Filesize { get; private set; }

    public string Name { get { return _Filewriter.Name; } }

    private async Task WriteToFile()
    {
        while (await _BufferBlock.OutputAvailableAsync())
        {
            byte[] data = _BufferBlock.Receive();
            await _Filewriter.WriteAsync(data, 0, data.Length);
            Filesize = _Filewriter.Length;
        }
    }

    private async Task Complete()
    {
        _BufferBlock.Complete();
        await Task.WhenAll(_WriteTask, _BufferBlock.Completion);
        //now close the file
        _Filewriter.Dispose();
    }

    public void Dispose()
    {
        Complete();
    }
}

要注意的重要事項是:

  • 我有連續的字節流,這些字節流通過回調從專有庫傳遞給我。 我不知道什么時候會收到這些數據。
  • 我需要能夠控制何時開始記錄此數據(按“ R”鍵)
  • 我需要能夠切換到新文件,但要確保先前的數據已寫入最后一個文件(在已記錄的同時再次按“ R”)

我有幾個問題,因為我是TPL數據流庫的新手。

  1. 首先,我的Producer / Consumer(即AsyncFileWriter)實現是否健全?
  2. 我的IDisposable實現可以嗎?
  3. 如何處理使用者中的異常(即WriteToFile)? 如果出現問題,確實需要通知我,但是您可以看到,唯一的公共方法是Produce() ,並且這里不會發生異常...這種模式是否有最佳實踐?

Dispose有點壞,因為它不等到所有活動都關閉后再進行操作。 這可能不是您想要的。 看到http://blog.stephencleary.com/2013/03/async-oop-6-disposal.html

AsyncFileWriter具有無限制的緩沖區。 如果文件編寫器過載,將消耗越來越多的內存。

否則,AsyncFileWriter似乎可以。

但是由於文件系統具有自己的緩沖功能,因此您可能根本不用任何像AsyncFileWriter這樣的東西就可以擺脫困境。 僅寫入文件可能就足夠了。

關於例外:您可以為此公開一個事件。 或者,如果發生異常,則關閉編寫,記住該異常,並使類的使用者可以檢查該異常。 此外,生產必須開始失敗。 處置不應失敗,但是在處置該類的用戶之前,需要關閉它並檢查異常。

因此,要么使用事件,要么將類移到錯誤狀態。

對於消費者來說,錯誤狀態可能更好一些,因為異常是在閑暇時發現的,而不是通過事件推送的。 Complete應該公開。 調用方應等待它,並在此處引發異常。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM