使用ParallelFor改变列表视图
本文关键字:视图 列表 改变 ParallelFor 使用 | 更新日期: 2023-09-27 18:14:27
我有一个函数修改我的listview "inputList", listview有5列。该函数应该检查列表视图中的单元格,最后一列的单元格将具有绿色背景的"NORMAL",如果没有什么问题,如果有什么问题,它将在最后一列的单元格中具有红色背景的"ERROR",并且还将更改与其他列在同一行中的错误单元格的颜色。
有4k项,我知道有这么多项是没有意义的,因为用户不会阅读它们,但我被要求这样做。
我正在使用一个函数与后台工作者,但它不够快,认为并行会更快。但是当我尝试parallelFor时,它冻结了程序。
这是backgroundworker的函数,它正在工作,但太慢了:
private void bw3_DoWork(object sender, DoWorkEventArgs e)
{
String tempTrain = "";
String tempPredic = "";
String tempNet = "";
Boolean bTrain;
Boolean bPredic;
Boolean bNetErro;
int n;
int nNet = 0;
String tTrain = "";
String tPredic = "";
int nList = 0;
this.Invoke(new MethodInvoker(() => {
nList = inputList.Items.Count;
nNet = menuNetwork.Items.Count;
tTrain = dtrainTextBox.Text;
tPredic = dpredicTextBox.Text;
}));
for (int i = 0; i < nList; i++)
{
ListViewItem.ListViewSubItem temp1 = new ListViewItem.ListViewSubItem();
temp1.BackColor = Color.LightGreen;
temp1.Text = "NORMAL";
this.Invoke(new MethodInvoker(() =>
{
inputList.Items[i].SubItems[0].BackColor = Color.White;
inputList.Items[i].SubItems[1].BackColor = Color.White;
inputList.Items[i].SubItems[2].BackColor = Color.White;
tempTrain = String.Format("{0}''{1}", tTrain, inputList.Items[i].SubItems[1].Text);
tempPredic = String.Format("{0}''{1}", tPredic, inputList.Items[i].SubItems[2].Text);
tempNet = (String)inputList.Items[i].SubItems[0].Tag;
}));
bTrain = (!File.Exists(tempTrain));
bPredic = (!File.Exists(tempPredic));
bNetErro = !(int.TryParse(tempNet, out n));
if (!bNetErro)
{
if (!(n < nNet))
{
bNetErro = true;
}
}
this.Invoke(new MethodInvoker(delegate
{
if (bTrain) inputList.Items[i].SubItems[1].BackColor = Color.Red;
if (bPredic) inputList.Items[i].SubItems[2].BackColor = Color.Red;
if (bNetErro) inputList.Items[i].SubItems[0].BackColor = Color.Red;
if (bTrain | bPredic | bNetErro) { temp1.Text = "Erro"; temp1.BackColor = Color.Red; }
try { inputList.Items[i].SubItems[4] = temp1; }
catch (ArgumentOutOfRangeException) { inputList.Items[i].SubItems.Add(temp1); }
}));
}
}
这是ParallelFor函数,程序停止工作,即使我尝试一个非常小的例子,只有项目。
private void tent1()
{
ParallelOptions options = new ParallelOptions();
options.MaxDegreeOfParallelism = 4;
String tempTrain = "";
String tempPredic = "";
String tempNet = "";
String tTrain = dtrainTextBox.Text;
String tPredic = dpredicTextBox.Text;
Boolean bTrain;
Boolean bPredic;
Boolean bNetErro;
int n;
int nNet = networkList.Items.Count;
Parallel.For(0, inputList.Items.Count,
i =>
{
ListViewItem.ListViewSubItem temp1 = new ListViewItem.ListViewSubItem();
temp1.BackColor = Color.LightGreen;
temp1.Text = "NORMAL";
Console.WriteLine(i);
this.Invoke(new MethodInvoker(() =>
{
inputList.Items[i].SubItems[0].BackColor = Color.White;
inputList.Items[i].SubItems[1].BackColor = Color.White;
inputList.Items[i].SubItems[2].BackColor = Color.White;
tempTrain = String.Format("{0}''{1}", tTrain, inputList.Items[i].SubItems[1].Text);
tempPredic = String.Format("{0}''{1}", tPredic , inputList.Items[i].SubItems[2].Text);
tempNet = (String)inputList.Items[i].SubItems[0].Tag;
}));
bTrain = (!File.Exists(tempTrain));
bPredic = (!File.Exists(tempPredic));
bNetErro = !(int.TryParse(tempNet, out n));
if (!bNetErro)
{
if (!(n < nNet))
{
bNetErro = true;
}
}
this.Invoke(new MethodInvoker(delegate
{
if (bTrain) inputList.Items[i].SubItems[1].BackColor = Color.Red;
if (bPredic) inputList.Items[i].SubItems[2].BackColor = Color.Red;
if (bNetErro) inputList.Items[i].SubItems[0].BackColor = Color.Red;
if (bTrain | bPredic | bNetErro) { temp1.Text = "Erro"; temp1.BackColor = Color.Red; }
try { inputList.Items[i].SubItems[4] = temp1; }
catch (ArgumentOutOfRangeException) { inputList.Items[i].SubItems.Add(temp1); }
}));
});
}
如何将listview的所有项复制到没有引用的List中?我认为直接添加/修改listview和调用会减慢函数的速度,因此创建:List<ListViewItem> tList
我将修改tList中的所有内容,然后使用inputList.Items.AddRange(tList.ToArray());
这将删除循环内的所有调用。
ListViewItem[] tItemsTemp = null;
this.Invoke(new MethodInvoker(() =>
{
tItemsTemp = new ListViewItem[inputList.Items.Count];
inputList.Items.CopyTo(tItemsTemp, 0);
nList = inputList.Items.Count;
nNet = menuNetwork.Items.Count;
tTrain = dtrainTextBox.Text;
tPredic = dpredicTextBox.Text;
}));
List<ListViewItem> tList = new List<ListViewItem>(tItemsTemp);
ListViewItem[] tItems = (ListViewItem[]) tItemsTemp.Clone();
//Modifies the list or array of listviewitems
this.Invoke(new MethodInvoker(delegate
{
inputList.Items.Clear();
// Just one of them,not the 3,just showing how i would call them.
inputList.Items.AddRange(tItems);
inputList.Items.AddRange(tItemsTemp);
inputList.Items.AddRange(tList.ToArray());
}));
但是tItemsTemp,tItems,tList都是引用…如何在创建引用时进行复制?
混合UI和并行化并不能保证提供好的结果。UI本身就是一个瓶颈,因为它运行在一个唯一的线程上。更糟糕的是,您的包含一些磁盘IO操作(File.Exists)。
有一些事情你可能想尝试一下:
-
将整个循环包含在一个inputList中。BeginUpdate和一个inputList.EndUpdate。这可以防止每次添加项目时刷新列表。
-
尝试SynchronizationContext和Post方法,而不是老式的Invoke方法。这可能使UI调用更平滑。
backcolor = Color.White
在第二次调用。
如果不是更好,那么尝试将构建项和显示项的过程分开:
- 使用并行化填充要更新的项目列表(不是使用ForEach,而是针对LINQ查询使用.AsParallel()) 使用一个循环来更新你的ListView。
如果这还不能让你满意,你可能需要在虚拟模式下使用ListView来解决这个问题。这个答案给出了一些异步方式填充虚拟ListView的提示。