使用后台并行无限循环 C# 读取缓冲区 [关闭]

Posted

技术标签:

【中文标题】使用后台并行无限循环 C# 读取缓冲区 [关闭]【英文标题】:Read a buffer with background parallel infinite loop C# [closed] 【发布时间】: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 以来 【参考方案1】:

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

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

对于文件夹部分:

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

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

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

然后处理:

    一个。 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 进行通信。

以上是关于使用后台并行无限循环 C# 读取缓冲区 [关闭]的主要内容,如果未能解决你的问题,请参考以下文章

Qt 循环中加入QCoreApplication::processEvents退出后台运行

Javascript无延迟播放无限循环音频文件

c#并行foreach循环查找索引

Matlab GUI中的无限循环导致关闭GUI时Matlab冻结?

从管道读取的无限循环线程进入 timed_waiting

多线程信号[关闭]