【问题标题】:How can I run two processes for different listview values in the same listview box?如何在同一个列表视图框中为不同的列表视图值运行两个进程?
【发布时间】:2016-11-03 23:36:49
【问题描述】:

我正在开发一个更大的程序,但它对列表框中的每个值执行的操作需要很长时间。我想通过对同一个列表视图框中的多个值执行相同的过程来加快速度,基本上是尝试添加某种类型的并行操作逻辑来完成工作。

我创建了一个快速程序,它是我正在工作的更大程序的一个非常非常简单的版本,我希望有人可以建设性地为我指明正确的方向。

有没有一种方法可以使用列表视图框,我可以同时从列表的顶部和底部开始,让每个人按照自己的方式工作,直到他们在中间相遇?我想不出另一种方法来做到这一点。

这是我现在与后台工作人员一起做的示例“代码”,没什么特别的(再次,这是一个模拟程序,因为我正在处理的实际程序的“复杂性”。)

        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {

        ListViewItem lst = listView1.SelectedItems[0];

        foreach (ListViewItem user in listView1.Items)
        {
            MessageBox.Show(user.SubItems[0].Text);
        }

    }

    private void button1_Click(object sender, EventArgs e)
    {
        CheckForIllegalCrossThreadCalls = false; // I know, but this is an example program, so I'm using this for ny own sanity sake right now.
        backgroundWorker1.RunWorkerAsync();
    }

这是程序的 GUI 部分

我希望这是有道理的。如果没有,我可以进一步详细说明,但这是我想要做的事情的要点。

(我曾尝试在堆栈溢出中查找类似问题,但我没有发现这对我有帮助,因为它们要么使用不同的语言,要么因为它们超出了范围,所以我无法遵循我的程序,我不明白如何使他们的答案适应我的程序。)

更新有答案

如果这对将来有帮助,我接受了 cmets 中的建议,并在我的代码中实现了 Parallel.ForEach。因为它是一个列表视图,所以我的方法必须更有创意。

我的 ForEach 语句现在看起来像这样:

var opts = new ParallelOptions { MaxDegreeOfParallelism = Convert.ToInt32(Math.Ceiling((Environment.ProcessorCount * 0.50) * 1.0)) };

Parallel.ForEach<ListViewItem>(lstBackupUsers.Items.Cast<ListViewItem>(),opts, name => { // Long stuff to do here });

基本上,我在我的 backgroundworker 任务中删除了常规的“foreach”命令,并将其替换为上面的 Parallel.ForEach。作为参考,我不得不使用 ParallelOptions 来限制资源的数量(在这种情况下,我的资源的 50%。)

更新 2(有奇怪的问题)

所以上面的语句按预期工作并且运行良好,除了 Parallel.ForEach(...) 语句没有在我的整个列表框中重复当使用 MaxDegreeOfParallelism 参数时。如果我不在 ForEach 语句中使用它,那么我会收到“内存不足”异常,但如果我确实使用它,那么我的 ForEach 会运行几行然后它就会停止。

是这样的(对我来说):

如果我设置 MaxDegreeOfParallelism,它只会通过我的机器中的内核数与列表视图框重复(无论我告诉 MaxDegreeOfParallelism 使用什么值。)例如:

我有一个包含 20 行的列表视图框,我的计算机只重复其中 8 个然后 ForEach 停止(我的机器现在有 8 个内核。)

知道我还能做什么吗?

谢谢!

更新 3

我现在有var result = Parallel.ForEach,我正在检查result.IsCompleted,这帮助我找到了解决循环的正确方向。

【问题讨论】:

  • 你试过使用Parallel.ForEach吗?
  • 谢谢 gilmishal,我没看过。哎呀..点击返回有点太快了。所以我会做类似ListViewItem user List&lt;ListViewItem&gt; username = new List&lt;ListViewItem&gt;(user) 然后Parallel.ForEach(username, row =&gt; { // background_dowork stuff here? });
  • 如果您同时执行所有这些任务,那么 "我可以从顶部和底部开始吗..." 不会为解决方案添加任何内容。
  • 我希望它可以通过一次处理 2 个用户而不是一次处理一个用户来加速程序。如果程序从列表框的顶部和底部开始,这个逻辑是否不正确(向上)同时?

标签: c# winforms parallel-processing


【解决方案1】:

您不需要 Background-Worker 来执行此任务。正如 gilmishal 在评论中提到的,您可以使用 Parallel.Foreach。此类位于包含 Microsoft 的新异步 TAP 模式的任务命名空间内。如果你使用多线程,你应该看看这个命名空间。 Parallel.Foreach 在新线程中的方法主体内启动您的操作(如果这是有道理的)。所以你不需要从顶部和底部开始。循环几乎同时(并行)为列表中的每个元素启动。

Parallel.ForEach(username, row => { // do your long running stuff });

UDPATE:

Parallel.ForEach 默认不在额外线程中执行。所以 UI 可以冻结。为了避免这种行为,您应该在额外的任务中运行 Parallel.ForEach。这个article 指出了为什么不应该在 UI 线程中运行 Parallel.ForEeach。因此,要停止冻结 UI,请执行以下操作:

 private void button1_Click(object sender, EventArgs e)
 {
    Task.Factory.StartNew(() => {
        Parallel.ForEach(username, row => { // background_dowork stuff here? });    
    });
 }

确保您没有访问分离线程中的任何 UI 组件。

【讨论】:

  • 感谢 Sebi 抽出宝贵时间回答这个问题。我对此有一个后续问题(可能还有更多):我仍然想使用后台工作人员,对吗?否则我的程序现在的工作方式在执行任务时会被“冻结”......除非Parralel.ForEach 类是“新”后台工作人员? (抱歉,我目前从未听说过/使用过 Parallel 类)
  • @MichaelTucker 您好,Parallel.Foreach 不是新的 BackgroundWorker。这只是 System.Task 命名空间中包含的新 TAP 模式的一种可能性。如果您愿意,System.Task 是新的 BackgroundWorker。我会更新我的答案,这样你的表格就不会冻结。
  • 您先生,真是个天才。如果可以的话,我会给你两个大拇指。我将很快在这里添加一个关于我的外观的额外评论,但我会将你的标记为答案。再次感谢
  • @MichaelTucker 欢迎您。如果您还有任何问题......问;)
  • @MichaelTucker 嗨,你确定你的 Parallel.Foreach 已经完成了吗?我可能是 Parallel.Foreach 在您的几次迭代之后仍然运行。在许多情况下,Parallel.Foreach 在完成之前返回。要检查这个使用 var result = Parallel.Foreach(//your stuff);结果.IsCompleted;请检查这个。
猜你喜欢
  • 2015-05-11
  • 2015-08-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-16
  • 1970-01-01
相关资源
最近更新 更多