我在WinForms应用程序中运行了一些过程繁重的任务。问题是,在运行时,它会冻结UI(UI主线程)。
我还没有在C#中使用线程和委托那么多,这就是为什么我希望有人可以帮助我,如何处理这些过程繁重的任务,而不冻结UI,所以用户不认为应用程序在等待时崩溃了吗?
EG。我通过FrontController
拨打电话,需要时间:
_controller.testExportExcel(wrapper, saveDialog.FileName);
因为它正在创建一个Excel文件。在工作时,我不会让应用程序在UI上做出响应。
过程繁重任务的另一个例子可能是:
private void dataGridView_liste_DataBindingComplete(object sender, DataGridViewBindingCompleteEventArgs e)
{
if (e.ListChangedType != ListChangedType.ItemDeleted)
{
foreach (DataGridViewRow r in dataGridView_liste.Rows)
{
DataGridViewCellStyle red = dataGridView_liste.DefaultCellStyle.Clone();
red.BackColor = Color.LightGreen;
if (r.Cells["News"].Value != null && (bool)r.Cells["News"].Value == true)
r.DefaultCellStyle = red;
}
}
}
foreach
循环需要时间,并冻结UI。运行该进程并在完成时自动关闭的异步线程可能对我有用。但它是如何运作的?
答案 0 :(得分:3)
如何使用Task(如果目标.net 4)?这被视为BackgroundWorker类的替代,因为它支持嵌套(父/子任务),任务延续等。
E.g。
private void dataGridView_liste_DataBindingComplete(object sender,
DataGridViewBindingCompleteEventArgs e)
{
Task t = Task.Factory.StartNew(() =>
{
// do your processing here - remember to call Invoke or BeginInvoke if
// calling a UI object.
});
t.ContinueWith((Success) =>
{
// callback when task is complete.
}, TaskContinuationOptions.NotOnFaulted);
t.ContinueWith((Fail) =>
{
//log the exception i.e.: Fail.Exception.InnerException);
}, TaskContinuationOptions.OnlyOnFaulted);
}
答案 1 :(得分:2)
我回答了一个非常相似的问题here
归结为使用BackgroundWorker。
msdn提供了一个示例:
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
namespace SL_BackgroundWorker_CS
{
public partial class Page : UserControl
{
private BackgroundWorker bw = new BackgroundWorker();
public Page()
{
InitializeComponent();
bw.WorkerReportsProgress = true;
bw.WorkerSupportsCancellation = true;
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
}
private void buttonStart_Click(object sender, RoutedEventArgs e)
{
if (bw.IsBusy != true)
{
bw.RunWorkerAsync();
}
}
private void buttonCancel_Click(object sender, RoutedEventArgs e)
{
if (bw.WorkerSupportsCancellation == true)
{
bw.CancelAsync();
}
}
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
for (int i = 1; (i <= 10); i++)
{
if ((worker.CancellationPending == true))
{
e.Cancel = true;
break;
}
else
{
// Perform a time consuming operation and report progress.
System.Threading.Thread.Sleep(500);
worker.ReportProgress((i * 10));
}
}
}
private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if ((e.Cancelled == true))
{
this.tbProgress.Text = "Canceled!";
}
else if (!(e.Error == null))
{
this.tbProgress.Text = ("Error: " + e.Error.Message);
}
else
{
this.tbProgress.Text = "Done!";
}
}
private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.tbProgress.Text = (e.ProgressPercentage.ToString() + "%");
}
}
}
DoWork事件处理程序中运行的所有内容都是异步的 在ProgessChanged / RunWorkCompleted的事件处理程序中运行的所有内容都在UI线程上。
答案 2 :(得分:0)
对于您的第一个示例,拨打_controller.testExportExcel()
,BackgroundWorker
或Task Parallel Library
来电(即Task.Factory.StartNew(...)
)将适合满足您保持用户界面响应的要求。大量的例子浮出水面,包括其他答案。
对于你的第二个例子,你会发现你不能把它放在后台线程上,因为它似乎是操纵UI的代码。具体来说,如果您BackgroundWorker
的{{1}}事件处理程序的实现,或传递给DoWork
的委托,或普通旧线程的方法触及UI,您很可能(/某些?)获得一个异常,说明“跨线程操作无效”。
理由是in this question。但是我更惊讶的是,实际上这很慢,你想让它异步。可能有一些简单的方法可以使这些代码更具响应性 - Task.Factory.StartNew()
和Control.SuspendLayout()
会让人想起。