【发布时间】:2023-03-16 14:35:01
【问题描述】:
我目前正在处理一个多线程项目,该项目涉及多个并行运行的耗时任务。
每个任务都记录在同一个文件中,我需要保持整洁(以正确的顺序),所以我找到了一种“逐个任务”记录的方法:
- 当任务必须写入日志文件时,我将日志写入字典:
_logList[id].Add(message) - 此字典的 Key = ThreadID,Value = 具有此 ID 的工作人员的日志(字符串列表)
- ThreadID 来自
Thread.CurrentThread.ManagedThreadId
这是我启动 BackgroundWorkers 的方式:
foreach (MyTask task in List<MyTask> tasks)
{
BackgroundWorker bg = new BackgroundWorker();
bg.DoWork += bgworker_DoWork;
bg.RunWorkerCompleted += bgworker_RunWorkerCompleted;
List<object> arguments = new List<object>() { task, args1 };
bg.RunWorkerAsync(arguments);
}
bgworker_DoWork 委托是耗时的任务,写入日志。
然后,当一个任务结束时,BackgroundWorker 运行委托 bgworker_RunWorkerCompleted,这基本上结束了任务并写入了相应的日志,顺序正确:
Log.FlushWrite((int)result[2]); -- result[2] contains the threadId to flush
而FlushWrite只将threadId的日志列表写入日志文件:
WriteMessageToFile(_logList[id]);
大多数时候,这是可行的。但有时,我会看到一种奇怪的行为:
- 多个任务使用相同的 ThreadID
- 当 ThreadID X 的第一个任务 N 结束时,它调用 RunWorkerCompleted 委托,并刷新其日志 + 仅 ThreadID X 的下一个任务 N+1 的一部分(因为任务尚未结束)
- 当任务N+1真正结束时,它会刷新剩余的日志写入,但是日志文件因为不整洁而一团糟!
我知道托管线程 id 可能会被回收和重用,但 imo 发生这种情况的唯一方法是线程已经被重用(对 DoWork 的新调用),而正在运行的 BackgroundWorker 尚未调用 @ 987654328@
我说的对吗?这种情况有什么我不明白的地方吗?
在这种情况下,也许使用 ManagedThreadId 作为字典的键是一种不好的做法?
感谢您的帮助:)
【问题讨论】:
-
确实,当您 [可能] 有独特的任务时,为什么要使用 ManagedThreadId 作为键(仅对其唯一性的弱假设)?为什么不使用 MyTask 作为字典键?但是您仍然可以做得更好并将记录器传递给 gbWorker_doWork。最后,为什么要使用 BackgroundWorker?您正在开发一个 winforms 应用程序吗?
-
我认为使用 ManagedThreadId 是最简单的键。确实我的任务是独一无二的,所以我可以把它当作关键!传递记录器是什么意思,这怎么行?最后我只知道BackgroundWorker的多线程:我不是在开发一个winform应用程序,你认为我不应该使用这个类来完成我的任务吗?你有什么建议? :) 谢谢
-
如果您需要报告进度,我建议使用
Task.Run和async/await,而不是BackgroundWorker,并结合Progress<T>。您可以看到here 两个选项之间的实际比较。今天使用BackgroundWorkers 进行新开发的唯一理由是在维护旧版应用程序时,这些应用程序在过期版本的 .NET Framework 上运行。 -
@TheodorZoulias 感谢您的回答。我无法更改为 Task.Run 但如果我仍然使用 ManagedThreadId 作为密钥,您认为它会解决我的日志记录问题吗?或者你的意思是我应该使用 Progress
来写日志? -
对不起,你的问题太复杂了,我无法理解,所以我真的无法提供任何关于如何解决它的建议。我刚刚提到了
Task.Run和 async/await 作为过时BackgroundWorker的现代替代品。如果您不能使用Task.Run,那么使用Progress<T>的价值就会相应减少。BackgroundWorker配备了自己的进度报告机制,为什么要混用呢?
标签: c# multithreading backgroundworker