c# - c# TaskFactory ContinueWhenAll 在所有任务完成之前意外运行
问题描述
我有一个 C# 中的数据处理程序(.NET 4.6.2;用于 UI 的 WinForms)。我遇到了一种奇怪的情况,计算机速度似乎导致 Task.Factory.ContinueWhenAll 比预期更早地运行,或者某些任务在实际运行之前报告完成。正如你在下面看到的,我有一个最多 390 个任务的队列,一次队列中不超过 4 个。当所有任务都完成时,状态标签会更新为完成。ScoreManager 涉及从数据库中检索信息、执行多个客户端计算以及保存到 Excel 文件。
从我的笔记本电脑运行程序时,一切都按预期运行;从功能更强大的工作站运行时,我遇到了这个问题。不幸的是,由于组织限制,我可能无法在工作站上直接使用 Visual Studio 进行调试。有谁知道可能导致我调查的原因是什么?
private void button1_Click(object sender, EventArgs e)
{
int startingIndex = cbStarting.SelectedIndex;
int endingIndex = cbEnding.SelectedIndex;
lblStatus.Text = "Running";
if (endingIndex < startingIndex)
{
MessageBox.Show("Ending must be further down the list than starting.");
return;
}
List<string> lItems = new List<string>();
for (int i = startingIndex; i <= endingIndex; i++)
{
lItems.Add(cbStarting.Items[i].ToString());
}
System.IO.Directory.CreateDirectory(cbMonth.SelectedItem.ToString());
ThreadPool.SetMaxThreads(4, 4);
List<Task<ScoreResult>> tasks = new List<Task<ScoreResult>>();
for (int i = startingIndex; i <= endingIndex; i++)
{
ScoreManager sm = new ScoreManager(cbStarting.Items[i].ToString(),
cbMonth.SelectedItem.ToString());
Task<ScoreResult> task = Task.Factory.StartNew<ScoreResult>((manager) =>
((ScoreManager)manager).Execute(), sm);
sm = null;
Action<Task<ScoreResult>> itemcomplete = ((_task) =>
{
if (_task.Result.errors.Count > 0)
{
txtLog.Invoke((MethodInvoker)delegate
{
txtLog.AppendText("Item " + _task.Result.itemdetail +
" had errors/warnings:" + Environment.NewLine);
});
foreach (ErrorMessage error in _task.Result.errors)
{
txtLog.Invoke((MethodInvoker)delegate
{
txtLog.AppendText("\t" + error.ErrorText +
Environment.NewLine);
});
}
}
else
{
txtLog.Invoke((MethodInvoker)delegate
{
txtLog.AppendText("Item " + _task.Result.itemdetail +
" succeeded." + Environment.NewLine);
});
}
});
task.ContinueWith(itemcomplete);
tasks.Add(task);
}
Action<Task[]> allComplete = ((_tasks) =>
{
lblStatus.Invoke((MethodInvoker)delegate
{
lblStatus.Text = "Complete";
});
});
Task.Factory.ContinueWhenAll<ScoreResult>(tasks.ToArray(), allComplete);
}
解决方案
您正在这里创建无需等待或观察的即发即弃的任务:
task.ContinueWith(itemcomplete);
tasks.Add(task);
Task.Factory.ContinueWhenAll<ScoreResult>(tasks.ToArray(), allComplete);
该ContinueWith
方法返回一个Task
. 您可能需要将allComplete
延续附加到这些任务,而不是它们的前身:
List<Task> continuations = new List<Task>();
Task continuation = task.ContinueWith(itemcomplete);
continuations.Add(continuation);
Task.Factory.ContinueWhenAll<ScoreResult>(continuations.ToArray(), allComplete);
附带说明一下,如果您使用async/await而不是老式的ContinueWith
技术,您可以使您的代码大小减半并显着提高可读性Invoke((MethodInvoker)
。
另外:ThreadPool
为了控制并行度而设置线程数的上限是非常不可取的:
ThreadPool.SetMaxThreads(4, 4); // Don't do this!
您可以改用Parallel
该类。它可以MaxDegreeOfParallelism
很容易地控制。
推荐阅读
- python - 发生错误时如何重新执行 ThreadPoolExecutor 中的函数?
- java - Cassandra 节点修补期间的 NullPointerExceptions
- c# - 从正在运行的任务中获取堆栈跟踪
- listview - SPFx 如何识别列表视图的类型?
- java - EBCDIC 解包 comp-3 数据在 Java 中返回 40404**
- python - FileNotFoundError: [Errno 2] No such file or directory for different opening types
- python - 小马如何设置栏目评论
- css - 如何更改 .sub-head 的 z-index 以显示在悬停动画的 .navbar-fixed 下?
- python - Django:除非删除父模型,否则防止删除子模型
- reactjs - 如何使用提供的操作通过模拟 Axios 来编写一个笑话测试?