使用reportprogress时结果不正确

时间:2014-01-09 15:13:02

标签: c# winforms backgroundworker

我有listview的项目添加静态和文本值序列:1,2,3,4,5,6 当我选择项目时会有相应的后台工作线程执行

例如:我选择了3个项目,因此有3个后台工作线程执行

一切正常,直到我添加backgroundworker的reportprogress来显示progressBar所以当选择项目不正确时结果

例如:我选择3个值为text的项目为2,3,5,因此结果显示为2,2,5

我尝试调试但找不到错误

有人帮助我,请!!!!

private static int numOfTheards = 0;
BackgroundWorker[] threadArray;
List<string> lst = null;
int i ;


private void InitializeBackgoundWorkers()
{

    for (int f = 0; f < numOfTheards; f++)
    {
        threadArray[f] = new BackgroundWorker();
        threadArray[f].DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
        threadArray[f].RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker1_RunWorkerCompleted);
        threadArray[f].ProgressChanged +=new ProgressChangedEventHandler(backgroundWorker1_ProgressChanged);
        threadArray[f].WorkerReportsProgress = true;
        threadArray[f].WorkerSupportsCancellation = true;
    }
}



private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{           
    string kq = "";
    while (i < lst.Count)
    {
        kq = lst[i];
        (sender as BackgroundWorker).ReportProgress(i * 100 / listViewEx1.Items.Count);
        ++i;
        break;
    }
    labelX1.Invoke(new MethodInvoker(delegate { labelX1.Text += kq + "-"; }));    
}




private void buttonX1_Click(object sender, EventArgs e)
{
    i = 0;
    labelX1.Text = "";
    InitializeBackgoundWorkers();
    for (int a = 0; a < numOfTheards; a++)
    {
        if (threadArray[a].IsBusy)
        {
            threadArray[a].CancelAsync();
        }
        else
        {
            progressBarX1.Value = progressBarX1.Minimum;
            threadArray[a].RunWorkerAsync();
        }
    }
}



private void listViewEx1_SelectedIndexChanged(object sender, EventArgs e)
{
    labelX2.Text = "";
    numOfTheards = listViewEx1.SelectedItems.Count;
    threadArray = new BackgroundWorker[numOfTheards];

    lst = new List<string>();
    foreach (ListViewItem item in listViewEx1.SelectedItems)
    {
        lst.Add(item.SubItems[0].Text);
        labelX2.Text += item.SubItems[0].Text+".";
    }
}

private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    progressBarX1.Value = e.ProgressPercentage;
    progressBarX1.Text = "Processing......" + progressBarX1.Value.ToString() + "%";
}

1 个答案:

答案 0 :(得分:2)

您似乎在理解多线程方面遇到了严重问题。例如,您正在使用全局变量i,该变量由后台工作程序代码修改 - 是什么告诉您没有竞争条件? (显然存在竞争条件,否则你不会遇到问题。

至少标记i volatile,甚至更好,真正重新考虑您的代码设计。


顺便说一下,问题发生并不令人惊讶。我会解释原因:

您使用公共变量i来确保3个后台工作人员在列表中工作,但其中没有两个工作在同一个项目上。这显然不适用于您的情况,因为您没有实施任何正确的方法来确保只有一个后台工作人员可以单独处理一个条目。

示例:两名后勤工作人员开始工作。两者都得到值i == 0,因为你没有确保操作“在索引i获取项目”和“增量i”是原子的(意思是:视为没有其他线程可以干扰的一个操作。)

您可以做的是完全锁定i的访问权限:

private volatile int i;
private object iLock = new object();

...

while (i < lst.Count)
{
    int previousI;

    lock (iLock)
    {
        previousI = i;
        kq = lst[i];
        ++i;
    }

    (sender as BackgroundWorker).ReportProgress(previousI * 100 / listViewEx1.Items.Count);

    ...
}

那是做什么的?它创建一个锁定的部分 - 只有一个线程可以同时输入它。然后它会记住当前线程的i 本地的当前值,获取列表条目并递增i。所有这一切都已完成,因此一次只有一个线程可以访问i

在进一步的操作过程中,它使用previousI,因为i已经被锁定块之外的另一个线程修改过了。

为什么这段代码很糟糕
您有太多地方访问i,无法正确锁定它们。

我要做什么
我没有使用i,而是将每个后台工作人员处理的数据“分区”。例如,我将创建一个数组列表。列表中的每个条目都包含每个后台工作者的lst项数组。然后我会让每个后台工作人员自己在自己的列表上工作。


如何对数据进行分区

没有尝试任何东西,我会做以下事情:

按下按钮时会执行以下代码:

private void buttonX1_Click(object sender, EventArgs e)
{
    i = 0;
    labelX1.Text = "";
    InitializeBackgoundWorkers();
    for (int a = 0; a < numOfTheards; a++)
    {
        if (threadArray[a].IsBusy)
        {
            threadArray[a].CancelAsync();
        }
        else
        {
            progressBarX1.Value = progressBarX1.Minimum;
            threadArray[a].RunWorkerAsync();
        }
    }
}

此代码已经错误。您调用InitializeBackgroundWorkers实际初始化数组中的新后台工作程序,该数组是listViewEx1_SelectedIndexChanged中新创建的。没有用于取消此代码中的任何工作人员,因为他们无法正在运行,因为您只创建了它们。如果要在创建新工作程序之前取消正在运行的工作程序,则需要在listViewEx1_SelectedIndexChanged中创建新工作程序数组之前取消它们。但这只是一个侧面说明。

初始化工人后,生成数据以处理每个工人的 。我知道您希望每个工作人员处理列表视图中所选项目的SubItem[0]。已处理的项目列表已在listViewEx1_SelectedIndexChangedlst中生成。因为现在lst中的每个项目都有一个后台工作者,这归结为:

private void buttonX1_Click(object sender, EventArgs e)
{
    i = 0;
    labelX1.Text = "";
    InitializeBackgoundWorkers();

    progressBarX1.Value = progressBarX1.Minimum;
    for (int a = 0; a < numOfTheards; a++)
    {
        string itemData = lst[a];
        threadArray[a].RunWorkerAsync(itemData);
    }
}