【问题标题】:Read a buffer with background parallel infinite loop C# [closed]使用后台并行无限循环 C# 读取缓冲区 [关闭]
【发布时间】:2021-05-27 17:26:39
【问题描述】:

我必须创建一个 WPF 应用程序 (.NET Framework) 来创建一个带有用户界面的程序。

当 GUI 工作时,程序必须继续,无限循环,在后台读取一个文件夹,该文件夹不断填充来自外部程序的新 txt 文件。

一个例子:

  • 外部程序 --> 创建新的 txt 文件并将它们放入 名为“缓冲区”的文件夹
  • 我的程序 --> 在后台启动一个无限循环,读取内容 每个文件并从“缓冲区”中删除它们

此进程必须由主 GUI 控制,但不必停止其他进程。

我想我必须使用线程来并行化进程,但不幸的是我没有太多的 C# 经验,请你帮我提些建议好吗?

【问题讨论】:

  • 忘记“无限循环”。您要么想通过例如 Timer 启动进程“运行”,要么使用 FileSystemWatcher
  • 注意:您可能迟早会发现您正在尝试处理仍在“传输中”的文件。您可能想从一开始就考虑这一点。
  • @GazTheDestroyer 我推荐IProgress / Progress
  • 您可能感兴趣的另一件事是Dataflow
  • @GazTheDestroyer 不,Task.Run 不会启动新线程,它将使用线程池中的线程。与 2010 年引入任务之前的 Dispatcher.BeginInvoke kind-of-did 相同。不再需要使用 BeginInvoke,绝对不是自 2012 年引入 await 以来

标签: c# wpf multithreading


【解决方案1】:

我以前一直站在你的立场上。我不能分享实际的代码(当然),但这就是我所做的:

  • 将“监视文件夹”至少分离到它自己的类中(如果不是子项目)。
  • 将“处理文件”至少分离到它自己的类中。

对于文件夹部分:

  1. 我使用FileSystemWatcher 来获取文件夹中存在新文件的通知。 EventHandler 所做的只是将文件路径放入ConcurrentDictionary<string,DateTime>,将“最后一次看到”与路径一起存储为键。

  2. 每隔 5 秒(我认为是)运行的计时器过滤字典中所有超过 2.5 秒(半间隔)的条目。这是为了确保文件不再在传输中。不是真正的“100% 解决方案”,但效果很好。

    选定的文件(路径字符串)被设置到“黑名单”中,以便在后续运行中被忽略,并发送到 Dataflow 管道。

然后处理:

  1. 一个。 data flow pipeline 完成了所有文件处理并最终发出完成的信号,因此可以将文件移动到“完成”文件夹并从黑名单中删除。

    b.导致异常的文件与包含异常消息和堆栈跟踪的相应“filename.error”文件一起放入“需要手动交互”文件夹中,并从黑名单中删除。请注意,您可能需要处理重复的文件名。

唯一的区别是我必须在控制台应用程序中执行此操作,该应用程序在服务器上连续运行。后来我将它重构为一个真正的 Windows 服务。但是,您必须处理 WPF。不过,基本思想应该保持不变。您可能需要的只是一种显示流程并与之交互的正确方法。我强烈建议您关注MVVM pattern

我还强烈建议采用其中一种漂亮的日志记录框架。

【讨论】:

    【解决方案2】:

    如果您真的需要一个无限循环对文件进行一些后台工作,则不需要在现代 .NET 中显式使用线程。您可以使用 TPL 并启动一个后台任务,该任务在您的应用启动后运行无限循环。在无限循环中,您应该定期检查取消令牌以使后台任务能够正常完成。

    我认为参考 TPL 文档是有道理的:https://docs.microsoft.com/en-US/dotnet/standard/parallel-programming/task-parallel-library-tpl

    高级代码示例:

    // Start your background task
    void StartBackgroundWorker()
    {
      // _cts is a data members of some object that encapsulates the background activity
      _cts = new CancellationTokenSource();
      System.Threading.Tasks.Task.Run(() => TaskEntryPoint(cts.Token));
    }
    
    // Stop your background task
    void StopBackgroundWorker()
    {
      _cts.Cancel();
      _cts.Dispose();
    }
    
    // In the background task, check for cancellation
    void TaskEntryPoint(CancellationToken token)
    {
      while (!token.IsCancellationRequested)
      {
        // complete a chunk of your background processing work
        // and check whether cancellation was requested by the 
        // owner of the background worker
      }
    }
    

    【讨论】:

    • 不需要此代码。它也将任务视为线程。任务不是线程,它们没有入口点。在字段中存储任务是不好的做法。没有理由在任务中运行循环。所有这些代码都可以替换为while(!token.IsCancellationRequested){ await Task.Run(()=>someHeavyWork);}
    • 不是我的反对意见,而是...您发布的是尝试使用任务重写 BackgroundWorker。 BGW 的问题不在于实现,而是整个概念
    • 也不是反对者,而是don't useTask.Factory.StartNew 方法。请改用Task.Run。也不要检查token.IsCancellationRequested 条件,因为它通常会导致不一致的取消行为。请改用ThrowIfCancellationRequested。根据标准取消模式,兑现取消信号与取消操作抛出的OperationCanceledException 进行通信。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-18
    • 1970-01-01
    • 2020-06-05
    • 1970-01-01
    • 2012-03-15
    • 1970-01-01
    相关资源
    最近更新 更多