簡體   English   中英

即使使用BackgroundWorker,表單也會凍結,除非我們添加了假睡眠

[英]Form freezes despite using BackgroundWorker unless we add a fake sleep

我已經成功地使BackgroundWorkerWinForm上完成了工作。 它工作正常,但實際上並非如此。 如下所示,我的表單具有一個listbox和一個Progress Bar 而且我正在使用它們兩者來顯示在for loop在文件上寫入收件人列表的進度。 我知道這個過程運行得太快了。 每次寫一行時, listbox顯示“正在從y個收件人中添加x個 ”消息,並刪除其自身的最后一個條目,以免文本過多。 同樣對於每個插入, progress bar也必須邁出一步。

在此處輸入圖片說明

當我使用Thread.Sleep(1)在每次寫入中添加1ms延遲時,程序運行正常。 我的表單仍然可以在桌面上移動,並且表單看起來很正常。 但是,當我刪除此睡眠(這是真實的情況)時,該表單將凍結,好像我從未使用過並行的BackgroundWorker 我該如何克服這個問題?

這分別是我的BackgroundWorker的DoWork()ProgressChanged()事件:

做工作:

private void backgroundWorkerConvertDatatableToFile_DoWork(object sender, 
                                                           DoWorkEventArgs e)
{
    try
    {
        StringBuilder sb = new StringBuilder();
        IEnumerable<string> columnNames = dt.Columns.Cast<DataColumn>().
                                            Select(column => column.ColumnName);
        sb.AppendLine(string.Join(GetDelimiter(campaignOutputFormat), columnNames));
        backgroundWorkerConvertDatatableToFile.ReportProgress(-1, 
                                   dt.Rows.Count.ToString() + " recipients found.");
        for (int i = 0; i < dt.Rows.Count; i++)
        {
           IEnumerable<string> fields = dt.Rows[i].ItemArray.Select(
                                                    field => field.ToString());
           sb.AppendLine(string.Join(GetDelimiter(campaignOutputFormat), fields));
           backgroundWorkerConvertDatatableToFile.ReportProgress(i, 
            string.Format("Adding {0} of {1}...", (i + 1).ToString(), dt.Rows.Count));
           Thread.Sleep(1);
        }
        string outputFile = String.Format("{0}\\{1}.csv", 
                                            campaignOutputPath, campaignFileName);
        backgroundWorkerConvertDatatableToFile.ReportProgress(0, "Writing to file..");
        File.WriteAllText(outputFile, sb.ToString());
        convertSuccess = true;
    }
    catch (Exception ex)
    {
        logger.Log(LogLevel.Error, 
                   "FileCampaignRunner: ConvertDataTableToCSV", ex.Message);
        convertSuccess = false;
    }
    if (convertSuccess)
    {
        backgroundWorkerConvertDatatableToFile.ReportProgress(100, 
                                               "Write to file successful!");
    }
    else
    {
        backgroundWorkerConvertDatatableToFile.ReportProgress(100, 
                                               "Error writing to file.");
    }
}

ProgressChanged:

private void backgroundWorkerConvertDatatableToFile_ProgressChanged(object sender,
                                                    ProgressChangedEventArgs e)
{
    switch (e.ProgressPercentage)
    {
        case -1:
            listBoxMessages.Items.Add(e.UserState.ToString());
            listBoxMessages.Items.Add("");
            break;
        case 100:
            listBoxMessages.Items.RemoveAt(listBoxMessages.Items.Count - 1);
            listBoxMessages.Items.Add(e.UserState.ToString());
            break;
        default:
            listBoxMessages.Items.RemoveAt(listBoxMessages.Items.Count - 1);
            listBoxMessages.Items.Add(string.Format(e.UserState.ToString()));
            progressBar.PerformStep();
            break;
    }
}

當您經常調用ReportProgress()時,這是完全正常的。 一個firehose問題 ,您要求UI線程做更多的工作,然后才能夠執行。 一旦執行了一個委托目標,然后又有一個委托目標,它將永遠無法實現。 現在,它停止執行其他任務,以響應輸入和繪制窗口。 僅在分發代表時,它就消耗了100%的核心資源。 調用隊列不斷增長,但是用戶在程序耗盡內存之前就失去了耐心,因此很少發生實際崩潰。

您需要通過不太頻繁地調用ReportProgress()來解決此問題。 請記住,您只需要實現一個目標,就可以使用戶滿意。 這很容易,您的輸出變成每秒約20次更新的不可讀的模糊。 輕松獲得50倍的安全系數。

這是因為UI線程阻塞了執行線程(同時訪問UI控件)。 試試下面的代碼

        switch (e.ProgressPercentage)
        {
            case -1:
                listBoxMessages.Invoke(new MethodInvoker(delegate
                {
                    listBoxMessages.Items.Add(e.UserState.ToString());
                    listBoxMessages.Items.Add("");
                }));
                break;
            case 100:
                listBoxMessages.Invoke(new MethodInvoker(delegate
                {
                    listBoxMessages.Items.RemoveAt(listBoxMessages.Items.Count - 1);
                    listBoxMessages.Items.Add(e.UserState.ToString());
                }));
                break;
            default:
                listBoxMessages.Invoke(new MethodInvoker(delegate
                {
                   listBoxMessages.Items.RemoveAt(listBoxMessages.Items.Count - 1);
                   listBoxMessages.Items.Add(string.Format(e.UserState.ToString()));
                }));
                progressBar.Invoke(new MethodInvoker(delegate
                {
                progressBar.PerformStep();
                }));
                break;
        }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM