Quickest way to Update Multiline Textbox with Larg

2019-05-28 20:39发布

问题:

I have a .NET 4.5 WinForm program that queries a text-based database using ODBC. I then want to display every result in a multiline textbox and I want to do it in the quickest way possible.
The GUI does not have to be usable during the time the textbox is being updated/populated. However, it'd be nice if I could update a progress bar to let the user know that something is happening - I believe a background worker or new thread/task is necessary for this but I've never implemented one.

I initially went with this code and it was slow, as it drew out the result every line before continuing to the next one.

OdbcDataReader dbReader = com.ExecuteReader();
while (dbReader.Read())
{
   txtDatabaseResults.AppendText(dbReader[0].ToString());
}

This was significantly faster.

string resultString = "";
while (dbReader.Read())
{
   resultString += dbReader[0].ToString();
}
txtDatabaseResults.Text = resultString;

But there is a generous wait time before the textbox comes to life so I want to know if the operation can be even faster. Right now I'm fetching about 7,000 lines from the file and I don't think it's necessary to switch to AvalonEdit (correct me if my way of thinking is wrong, but I would like to keep it simple and use the built-in textbox).

回答1:

You can make this far faster by using a StringBuilder instead of using string concatenation.

var results = new StringBuilder();
while (dbReader.Read())
{
    results.Append(dbReader[0].ToString());
}
txtDatabaseResults.Text = results.ToString();

Using string and concatenation creates a lot of pressure on the GC, especially if you're appending 7000 lines of text. Each time you use string +=, the CLR creates a new string instance, which means the older one (which is progressively larger and larger) needs to be garbage collected. StringBuilder avoids that issue.

Note that there will still be a delay when you assign the text to the TextBox, as it needs to refresh and display that text. The TextBox control isn't optimized for that amount of text, so that may be a bottleneck.

As for pushing this into a background thread - since you're using .NET 4.5, you could use the new async support to handle this. This would work via marking the method containing this code as async, and using code such as:

string resultString = await Task.Run(()=>
{
    var results = new StringBuilder();
    while (dbReader.Read())
    {
        results.Append(dbReader[0].ToString());
    }
    return results.ToString();
});

txtDatabaseResults.Text = resultString;


回答2:

Use a StringBuilder:

StringBuilder e = new StringBuilder();
while (dbReader.Read())
{
   e.Append(dbReader[0].ToString());
}
txtDatabaseResults.Text = e.ToString();


回答3:

Despite the fact that a parallel Thread is recommended, the way you extract the lines from file is somehow flawed. While string is immutable everytime you concatenate resulString you actually create another (bigger) string. Here, StringBuilder comes in very useful:

StringBuilder resultString = new StringBuilder ()
while (dbReader.Read())
{
   resultString = resultString.Append(dbReader[0].ToString());
}
txtDatabaseResults.Text = resultString;


回答4:

I am filling a regular TextBox (multiline=true) in a single call with a very long string (more than 200kB, loaded from a file. I just assign the Text property of TextBox with my string). It's very slow (> 1 second). The Textbox does anything else than display the huge string.

I used a very simple trick to improve performances : I replaced the multiline textbox by a RichTextBox (native control).

Now same loadings are instantaneous and RichTextBox has exactly the same appearance and behavior as TextBox with raw text (as long as you didn't tweaked it). The most obvious difference is RTB does not have Context menu by default.

Of course, it's not a solution in every case, and it's not aiming the OP question but for me it works perfectly, so I hope it could help other peoples facing same problems with Textbox and performance with big strings.