تقييم الموضوع :
  • 0 أصوات - بمعدل 0
  • 1
  • 2
  • 3
  • 4
  • 5
Threading in Windows Forms Applications
#2


يظهر لك المثال التالي كيف يمكن استخدام العنصر BackgroundWorker للبحث عن الملفات في مسار غير متزامن وهي نفس المشكلة التي طرحت عند الحديث عن The ISynchronizeInvoke Interface في هذا الموضوع سابقا وبهذا يمكنك مقارنة الطريقتين بسهولة. وستكون النسخة الجديدة المعتمدة على BackgroundWorker أكثر تعقيدا بقليل بسبب أنها تدعم الإلغاء لعمل غير متزامن

كود :
' The result from the SearchFiles procedure
Dim files As List(Of String)
' We need this variable to avoid nested calls to ProgressChanged.
Dim callInProgress As Boolean

' The same button works as a Start and a Stop button.
Private Sub btnStart_Click(ByVal sender As Object, ByVal e As EventArgs) _
Handles btnStart.Click
If btnStart.Text = "Start" Then
lstFiles.Items.Clear()
Me.BackgroundWorker1.RunWorkerAsync("c:\windows")
Me.btnStart.Text = "Stop"
Else
Me.BackgroundWorker1.CancelAsync()
End If
End Sub

' The code that starts the asynchronous file search
Private Sub BackgroundWorker1_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs) _
Handles BackgroundWorker1.DoWork
' Retrieve the argument.
Dim path As String = e.Argument.ToString()
' Invoke the worker procedure.
files = New List(Of String)
SearchFiles(path)
' Return a result to the RunWorkerCompleted event.
Dim msg As String = String.Format("Found {0} files", files.Count)
e.Result = msg
End Sub

' A recursive function that retrieves all the files in a directory tree.
Sub SearchFiles(ByVal path As String)
' Display a message.

Dim msg As String = String.Format("Parsing directory {0}", path)
' Notice that we don't really use the percentage;
' instead, we pass the message in the UserState property.
Me.BackgroundWorker1.ReportProgress(0, msg)

' Read the files in this folder and all subfolders.
' Exit immediately if the task has been canceled.
For Each fi As String In Directory.GetFiles(path)
If Me.BackgroundWorker1.CancellationPending Then Return
files.Add(fi)
Next
For Each di As String In Directory.GetDirectories(path)
If Me.BackgroundWorker1.CancellationPending Then Return
SearchFiles(di)
Next
End Sub

Private Sub BackgroundWorker1_ProgressChanged(ByVal sender As Object, _
ByVal e As ProgressChangedEventArgs) _
Handles BackgroundWorker1.ProgressChanged
' Reject nested calls.
If callInProgress Then Return
callInProgress = True
' Display the message, received in the UserState property.
Me.lblMessage.Text = e.UserState.ToString()
' Display all files added since last call.
For i As Integer = lstFiles.Items.Count To files.Count - 1
lstFiles.Items.Add(files(i))
Next
Me.Refresh()
' Let the Windows operating system process message in the queue.
' If you omit this call, clicks on buttons are ignored.
Application.DoEvents()
callInProgress = False
End Sub

Private Sub BackgroundWorker1_RunWorkerCompleted(ByVal sender As Object, _
ByVal e As RunWorkerCompletedEventArgs) _
Handles BackgroundWorker1.RunWorkerCompleted
' Display the last message and reset button's caption.
Me.lblMessage.Text = e.Result.ToString()
btnStart.Text = "Start"
End Sub
والكود هنا يشرح نفسه ماعدا إجراء الحدث ProgressChanged حيث يجب أن يتضمن الكود استدعاء للطريقة Application.DoEvents وإلا لن يتمكن التطبيق من معالجة الأحداث المنطلقة مثل حدث النقر على الزر Stop أو أي عمل آخر ممكن إضافته للواجهة ومع ذلك فاستدعاء هذه الطريقة سيسبب استدعاءات معششة للإجراء ProgressChanged مما قد يسبب إطلاق استثناء StackOverflowException ومن أجل عدم حدوث هذا يتم استخدام حقل منطقي مساعد callInProgress لتجنب حدوث مثل هذه الاستدعاءات المعششة
لاحظ أيضا أن هذا التطبيق لا يحتاج للإعلام عن نسبة التقدم للمسار الرئيسي ويستخدم الطريقة ReportProgress فقط لتنفيذ جزء من الكود في المسار الرئيسي للبرنامج والرسالة الفعلية للإظهار يتم تمريرها للخاصية UserState وإن كان تطبيقك يستخدم progress bar أو أي مؤشر آخر للتقدم يجب عليك تجنب استدعاء الطريقة ReportProgress بدون داعي لأنها تتسبب بتبديل المسارات وتكون مكلفة كثيرا عندما يتعلق الأمر بوقت المعالجة وفي هذه الحالة يجب عليك تخزين مؤشر التقدم في حقل في الفئة واستدعاء الطريقة فقط في حالة حدوث تقدم فعلي

كود :
Dim currentPercentage As Integer

Private Sub BackgroundWorker1_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs) _
Handles BackgroundWorker1.DoWork
Const TotalSteps = 5000
For i As Integer = 1 To TotalSteps

' Evaluate progress percentage.
Dim percentage As Integer = (i * 100) \ TotalSteps
' Report to UI thread only if percentage has changed.
If percentage <> currentPercentage Then
BackgroundWorker1.ReportProgress(percentage)
currentPercentage = percentage
End If
Next
End Sub
}}}
تم الشكر بواسطة:


الردود في هذا الموضوع
Threading in Windows Forms Applications - بواسطة Raggi Tech - 01-10-12, 07:36 PM
Threading in Windows Forms Applications - بواسطة Raggi Tech - 01-10-12, 07:36 PM


التنقل السريع :


يقوم بقرائة الموضوع: