繁体   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