C# 如何在Winform中使用多线程?
我是个多线程新手。我有一个winform,它有一个标签和一个进度条 我想显示处理结果。首先,我使用C# 如何在Winform中使用多线程?,c#,.net,winforms,multithreading,C#,.net,Winforms,Multithreading,我是个多线程新手。我有一个winform,它有一个标签和一个进度条 我想显示处理结果。首先,我使用Application.DoEvents()方法。但是,我发现表格冻结了 然后我在MSDN上读了一些关于多线程的文章 第二,我使用后台人员来完成这项工作 this.bwForm.DoWork += (o, arg) => { DoConvert(); }; this.bwForm.RunWorkerAsync(); 表单不会冻结,因此我可以在处理时拖动/拖动。不幸的是,它抛出了一个Inval
Application.DoEvents()
方法。但是,我发现表格冻结了
然后我在MSDN上读了一些关于多线程的文章
第二,我使用后台人员来完成这项工作
this.bwForm.DoWork += (o, arg) => { DoConvert(); };
this.bwForm.RunWorkerAsync();
表单不会冻结,因此我可以在处理时拖动/拖动。不幸的是,它抛出了一个InvalidOperationException。所以我必须用这个<代码>Control.CheckForIllegalCrossThreadCalls=false代码>我确保这不是最终解决方案
专家们,你们有什么建议吗
编辑:
调用listview时抛出InvalidOperationException。此代码位于DoWork()中
Edit2:
我在DoWork()中使用delegate和invoke,它不会抛出exeption。但是这个表格又冻结了。如何使表单在处理时可以拖放?您应该使用。有关详细信息,请参见。调用UI线程。例如:
void SetControlText(Control control, string text)
{
if (control.InvokeRequired)
control.Invoke(SetControlText(control, text));
else
control.Text = text;
}
这样做,创建一个新线程
Thread loginThread = new Thread(new ThreadStart(DoWork));
loginThread.Start();
在ThreadStart()中,传递要执行的方法。如果在该方法中要更改某些控件属性,请创建一个委托,并将其指向将在其中写入控件更改属性的方法
public delegate void DoWorkDelegate(ChangeControlsProperties);
调用控件属性,声明一个方法,并在其中定义控件的新属性
public void UpdateForm()
{
// change controls properties over here
}
然后用这种方式将委托指向该方法
InvokeUIControlDelegate invokeDelegate = new InvokeUIControlDelegate(UpdateForm);
那么当你想在任何地方改变属性的时候就叫这个
this.Invoke(invokeDelegate);
希望此代码片段对您有所帮助!:) 避免调用
应用程序.DoEvents
。通常情况下,这会导致更多的问题,而不是解决问题。这通常被认为是一种不好的做法,因为存在更好的替代方案来保持UI的消息传递
避免更改设置CheckForIllegalCrossThreadCalls=false
。这并不能解决任何问题。这只会掩盖问题。问题是您试图从主UI线程以外的线程访问UI元素。如果禁用CheckForIllegalCrossThreadCalls
,那么您将不再获得异常,而是应用程序将以不可预测的方式异常失败
在
DoWork
事件处理程序中,您需要定期调用ReportProgress
。从表单
中,您需要订阅ProgressChanged
事件。从ProgressChanged
事件处理程序中访问UI元素是安全的,因为它会自动封送到UI线程。您只能从UI线程(WinForm线程)设置进度条用户控件属性。
使用BackgroundWorker执行此操作的最简单方法是使用ProgressChanged事件:
private BackgroundWorker bwForm;
private ProgressBar progressBar;
在WinForm构造函数中:
this.progressBar = new ProgressBar();
this.progressBar.Maximum = 100;
this.bwForm = new BackgroundWorker();
this.bwForm.DoWork += new DoWorkEventHandler(this.BwForm_DoWork);
this.bwForm.ProgressChanged += new ProgressChangedEventHandler(this.BwForm_ProgressChanged);
this.bwForm.RunWorkerAsync();
请看这里:
编辑
所以我不明白你的问题,我以为是关于在工作中显示进度条的。
如果是关于工作结果的,请使用e。导致BwForm_DoWork事件。
为已完成的事件和管理结果添加新的事件处理程序:
this.bwForm.RunWorkerCompleted += new RunWorkerCompletedEventHandler(this.BwForm_RunWorkerCompleted);
最干净的方法是像您一样使用
BackGroundWorker
你只是错过了一些要点:
DoWork
事件处理程序中访问表单元素,因为这会导致跨线程方法调用,这应该在ProgressChanged
事件处理程序中完成BackGroundWorker
将不允许报告进度,也不允许取消操作。在代码中添加BackGroundWorker
时,如果要调用BackGroundWorker
的ReportProgress
方法,则必须将该BackGroundWorker
的WorkerReportsProgress
属性设置为true
WorkerSupportsCancellation
设置为true,并在DoWork
事件处理程序的循环中检查BackGroundWorker
中名为CancellationPending
的属性我希望我能提供帮助您所展示的代码存在一些基本问题。正如其他人提到的,
Application.DoEvents()
不会从后台线程执行您想要的操作。将缓慢的后台处理分离为BackgroundWorker
,并在UI线程中更新进度条。您正在从后台线程调用progressBar1.Value++
,这是错误的
永远不要调用Control.CheckForIllegalCrossThreadCalls=false,这只会隐藏错误
如果要从后台线程更新进度条,则必须实现ProgressChanged
处理程序;你没有那样做
如果您需要实现
BackgroundWorker
的示例,请参阅code项目中的文章 再加上自由和平的解决方案,他是对的。此解决方案是线程安全的,这正是您想要的。您只需要使用BeginInvoke
而不是Invoke
void SetControlText(Control control, string text)
{
control.BeginInvoke(
new MethodInvoker(() =>
{
control.Text = text;
})
);
}
上述修复是最简单、最干净的
希望这有帮助。:) 我想说,编写线程安全的应用程序是更难理解和正确执行的事情之一。你真的需要好好读一读。如果你从一本书中学习了C#,那么回头看看是否没有关于多线程的章节。我从安德鲁·特罗尔森(Andrew Troelsen)的书中了解到(最新一本是Pro C#2005和.NET 2.0平台),他直到第14章才触及这个话题(因此当时很多人已经停止阅读)。我来自一个嵌入式编程背景,其中并发性和原子性也很重要,所以这不是一个.NET或Windows特定的问题 这里的很多帖子都有详细介绍
this.bwForm.RunWorkerCompleted += new RunWorkerCompletedEventHandler(this.BwForm_RunWorkerCompleted);
private void BwForm_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
YourResultStruct result = e.Result as YourResultStruct;
if (e.Error != null && result != null)
{
// Handle result here
}
}
void SetControlText(Control control, string text)
{
control.BeginInvoke(
new MethodInvoker(() =>
{
control.Text = text;
})
);
}
intVal++; //This is not thread safe
int newVal = Interlocked.Increment(ref intVal); //This is thread safe