简体   繁体   English

如何处理TPL数据流中的异常-生产者/消费者

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

I have the following test application that simulates my application scenario: 我有以下测试应用程序可以模拟我的应用程序场景:

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;
}

It uses the following class as the Consumer(/producer): 它使用以下类作为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();
    }
}

The important things to note are: 要注意的重要事项是:

  • I have a continuous stream of bytes that are passed to me from a proprietary library via a callback. 我有连续的字节流,这些字节流通过回调从专有库传递给我。 I do not know when I will receive this data. 我不知道什么时候会收到这些数据。
  • I need to be able to control when to start recording this data (press "R") 我需要能够控制何时开始记录此数据(按“ R”键)
  • I need to be able to switch to a new file, but ensure previous data has been written to the last file (press "R" again while already recording) 我需要能够切换到新文件,但要确保先前的数据已写入最后一个文件(在已记录的同时再次按“ R”)

I have a few questions, as I am quite new to the TPL dataflow library. 我有几个问题,因为我是TPL数据流库的新手。

  1. Firstly, is my implementation of Producer/Consumer (ie AsyncFileWriter) sound? 首先,我的Producer / Consumer(即AsyncFileWriter)实现是否健全?
  2. Is my implementation of IDisposable OK? 我的IDisposable实现可以吗?
  3. How do I handle exceptions in the Consumer (ie WriteToFile)? 如何处理使用者中的异常(即WriteToFile)? I really need to be notified if something goes wrong, but as you can see the only public method is Produce() , and the exception won't happen here... Is there a best practise for this pattern? 如果出现问题,确实需要通知我,但是您可以看到,唯一的公共方法是Produce() ,并且这里不会发生异常...这种模式是否有最佳实践?

Dispose is kind of broken because it does not wait until all activity is shut down. Dispose有点坏,因为它不等到所有活动都关闭后再进行操作。 This is probably not what you want. 这可能不是您想要的。 See http://blog.stephencleary.com/2013/03/async-oop-6-disposal.html . 看到http://blog.stephencleary.com/2013/03/async-oop-6-disposal.html

AsyncFileWriter has an unbounded buffer. AsyncFileWriter具有无限制的缓冲区。 If the file writer is overloaded this will consume more and more memory. 如果文件编写器过载,将消耗越来越多的内存。

AsyncFileWriter seems OK otherwise. 否则,AsyncFileWriter似乎可以。

But since the file system does its own buffering you maybe can get away with not using anything like AsyncFileWriter at all. 但是由于文件系统具有自己的缓冲功能,因此您可能根本不用任何像AsyncFileWriter这样的东西就可以摆脱困境。 Just writing to the file might be good enough. 仅写入文件可能就足够了。

Regarding exceptions: You can expose an event for that. 关于例外:您可以为此公开一个事件。 Or, in case of an exception you shut down writing, remember the exception and make it possible for consumers of the class to examine the exception. 或者,如果发生异常,则关闭编写,记住该异常,并使类的使用者可以检查该异常。 Also, Produce must start to fail. 此外,生产必须开始失败。 Dispose should not fail but before disposing users of the class need to shut it down and check for exceptions. 处置不应失败,但是在处置该类的用户之前,需要关闭它并检查异常。

So either use an event or move the class into an error state. 因此,要么使用事件,要么将类移到错误状态。

Probably, the error state is a little nicer for consumers because the exception is discovered at their leisure, not pushed through an event. 对于消费者来说,错误状态可能更好一些,因为异常是在闲暇时发现的,而不是通过事件推送的。 Complete should be public. Complete应该公开。 The caller should await it and the exception should be thrown here. 调用方应等待它,并在此处引发异常。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM