后台任务涉及网络I / O,磁盘I / O或其他可能会或可能不会通过网络发生的长时间运行的任务。它经常与更新GUI的代码混合,后者需要在另一个线程GUI线程上运行。
简单的意思是,在打开Form.cs文件时,源代码比以前更容易或更容易阅读。实际上,源代码的流程仍然必须按照执行代码的顺序顺序读取,而不管它在哪个线程上执行。所有支撑织物必须可以重复使用并隐藏在某个地方,而不是包含在每个表格中。
谷歌搜索MSDN:发现微软正式批准的解决方案是System.ComponentModel.BackgroundWorker,它在第二点上落下(非常!)。(在System.Windows.Threading.Dispatcher中还有官方批准的Silverlight / XAML / 3.5解决方案模型。)
答案 0 :(得分:3)
如果你真的不喜欢BackgroundWorker,你可以为后台操作创建自己的基类,就像我here一样。
答案 1 :(得分:2)
您仍然可以使用BackgroundWorker
。它不需要作为表单上的组件存在。您可以轻松地将其包装到一个类中,然后可以在每个表单中重复使用。
但是,这与在需要时简单地为后台任务设置工作程序没什么不同。
答案 2 :(得分:2)
使用BackgroundWorker代替
答案 3 :(得分:1)
你能解释为什么你说BackgroundWorker
不合适吗?
在大多数情况下,它需要2-3行额外的代码。
答案 4 :(得分:1)
这是我迄今为止提出的最简单的想法。它可能完全不是犹太人,我的编码非常接近零.Windows.Forms应用程序。
它涉及一个帮助器,它有两个主要方法,Background()和Foreground()。这些中的任何一个都采用以lambda表达式形式指定的委托。 Background()在后台线程上启动任何给定的委托并立即返回。 Foreground()使用Form.BeginInvoke()将任何给定的委托“返回”发送到GUI线程并立即返回。
以下是如何使用此设计模式的代码示例,前提是已经实现了帮助程序。
public class Form1 : Form {
protected ProgressBar progressBar1;
protected Button button1;
protected BackgroundHelper helper = new BackgroundHelper();
public void button1_Click(...) {
// Execute code in the background.
helper.Background(() => {
for (int i = 0; i <= 100; i++) {
// Continually report progress to user.
helper.Foreground<int>(i, j => {
progressBar1.Value = j;
});
// Simulate doing I/O or whatever.
Thread.Sleep(25);
}
});
}
}
这使代码整齐顺序,在一个好的位置提供共享变量,并允许跨越两个线程的循环。
澄清助手的作用,
答案 5 :(得分:0)
例如,关于磁盘I / O的问题,我最近编写了一个异步文件扫描程序类(或多年来第100次)。它可以像你想要的那样重复使用,只需实例化一个新实例并挂钩事件即可。
我将功能封装在实现跨线程安全的类中。该类触发事件以通知调用者更新以及何时完成。
你说“源代码仍然必须按顺序读取代码的执行顺序”,但要意识到线程彼此并行运行。这就是分离代码结构的好处。
这表明将可重用代码隔离到一个单独的类......
Public Class FileScanner
Public Event Scan_Complete(sender As Object)
Public Event Scan_Update(sender As Object, filename As String)
Public Event Scan_Error(sender As Object, ex As Exception)
Private Delegate Sub del_ScanComplete()
Sub New(syncObject As Control, path String)
Me.SynchronizeObject = syncObject
ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf ScanFilesAsync), path)
End Sub
Private Sub ScanFilesAsync(ByVal state As Object)
' scan files here
' call the method to raise the Complete event
ScanComplete()
End Sub
Private Sub ScanComplete()
If SynchronizeObject.InvokeRequired Then
' we cant raise event on a different thread than our caller
' so lets invoke our method on the same caller thread
Dim d As New del_ScanComplete(AddressOf ScanComplete)
SynchronizeObject.Invoke(d)
Else
' no synchronize needed, tell the caller we are done
RaiseEvent Complete(Me)
End If
End Sub
End Class
答案 6 :(得分:0)
在Windows窗体中执行后台任务并在完成事件时引发事件的替代方法是使用AsyncOperationManager和AsyncOperation类。要注意的是,必须在UI线程中创建AsyncOperation才能正常工作。否则,UI代码必须检查InvokeRequired。
public class BackgroundTask{
private AsyncOperation _asyncOperation;
public EventHandler Done;
public BackgroundTask(){
_asyncOperation = AsyncOperationManager.CreateOperation();
}
public void DoAsync(object userState) {
System.Threading.ThreadPool.QueueUserWorkItem( ExecuteDo, userState);
}
private void ExecuteDo(object state) {
// Do your work here
// Raise event after finish
_asyncOperation.PostOperationCompleted( Finished, EventArgs.Empty );
}
private void LookUpFinished( object eventargs ) {
OnDone( ( EventArgs) eventargs );
}
private void OnDone( LookUpEventArgs e ) {
EventHandler localEvent = Done;
if ( localEvent!= null ) {
localEvent(this,e);
}
}
}
以下是代码的使用方法:
public class MyForm : Form {
public MyForm() {
InitializeComponent();
}
protected override OnShown(EventArgs e) {
BackgroundTask task = new BackgroundTask();
task.Done += SignalTaskDone;
}
private void SignalTaskDone(object sender, EventArgs e){
MessageBox.Show(this, "Task finished");
}
}
答案 7 :(得分:0)
正如有说服力here所说,微软已经看到了BackgroundWorker中的缺陷,你可以使用Task.Run和async关键字获得更好,更清晰的代码。