تقييم الموضوع :
  • 0 أصوات - بمعدل 0
  • 1
  • 2
  • 3
  • 4
  • 5
نظرة عامة حول Event-based Asynchronous Pattern
#1
كاتب الموضوع : Islam Ibrahim

التطبيقات التي تقوم بتنفيذ عمليات متعددة في نفس الوقت تحتاج بطبيعة الحال لأن تبقى طوال الوقت مستجيبة ومستعدة لمعالجة العمليات التي يقوم بها المستخدم, يتطلب هذا الأمر عادة استخدام تعدد المسارات Multithreading , ومن أجل هذا تم تضمين مجالي الأسماء System.Threading و System.ComponentModel بمجموعة من الأدوات المناسبة لإنشاء التطبيقات متعددة المسارات ذات الأداء القوي, ولكن من أجل استخدام تلك الأدوات لا بد من أن يكون لديك دراية وخبرة كافية لتصميم تطبيق يعتمد على تعدد المسارات, وأحيانًا استخدام أداة في غير مكانها يتسبب في نتائج جانبية غير محمودة, ومن أجل ذلك يكون المكوّن BackgroundWorker هو الحل الأمثل في أغلب الأحيان, وهناك أحيان أخرى عند الحاجة لتنفيذ المهام بشكل غير متزامن تحتاج لبناء فئتك الخاصة وفقًا لنمط يُعرف بـ Event-based Asynchronous Pattern أو نمط العمليات الغير متزامنة والتي تعتمد على إطلاق الأحداث Events.

استخدام نمط العمليات الغير متزامنة بالاعتماد على الأحداث, يقدم لنا تلك المزايا التي تتمتع بها التطبيقات متعددة المسارات, في حين يخفي في طياته الكثير من التعقيدات التي يمكن أن تنشأ عند القيام ببناء التطبيقات المتعددة المسارات, وعندما تقوم ببناء فئتك الخاصة بالاعتماد على هذا النمط, سيصبح بإمكانك:
  • تنفيذ العمليات التي تستهلك وقتاً أطول أكثر من غيرها (مثل العمليات على قواعد البيانات, أو عمليات التحميل Downloading) في الخلفية بدون مقاطعة واجهة الاستخدام.
  • تنفيذ عمليات متعددة في نفس الوقت, واستقبال تنبيهات عند انتهاء كل عملية
  • الانتظار من أجل الحصول على مورد معيّن دون تعليق التطبيق وجعله عديم الاستجابة.
  • التخاطب مع العمليات الغير متزامنة أثناء تنفيذها بالاستعانة بالأحداث والمفوضات Events & Delegates.
عادة ما تحتوي الفئات التي تطبق هذا النمط على إجراء أو عدة إجراءات أسماؤهم بالشكل التالي: MethodNameAsync , يمكن أن تحتوي تلك الفئات أيضًا على أحداث أسماؤها من الشكل MethodNameCompleted ويمكن أن تحتوي أيضًا على إجراء باسم MethodNameCancelAsync أو ببساطة CancelAsync .

أداة التحكم PictureBox تعتبر مثالاً جيدًا لفئة تطبّق نمط العمليات الغير المتزامنة بالاعتماد على الأحداث , فيمكنك مثلاً تحميل صورة من موقع ما بشكل متزامن synchronously باستخدام الإجراء Load ولكن إذا كان حجم الصورة كبيرًا أو الاتصال بالإنترنت ضعيفًا فإن ذلك سيتسبب في توقف التطبيق عن الاستجابة Hanging حتى ينتهي تنفيذ الإجراء Load. وإذا أردت أن يحافظ التطبيق على استجابته ستحتاج عندها لاستخدام الإجراء LoadAsync , وهنا سيصبح بإمكانك معالجة الحدث LoadCompleted للأداة PictureBox تماماً كما تفعل مع أي حدث آخر, عندما تستدعي الإجراء LoadAsync فإن التطبيق يستمر في العمل والاستجابة وسيتم تنفيذ العملية في الخلفية في مسار مستقل عن المسار الأساسي لواجهة الاستخدام UI Thread , وسيتم استدعاء معالج الحدث LoadCompleted عند انتهاء العملية عندها يمكنك اختبار الوسيط من النوع AsyncCompletedEventArgs والذي سُيمرر لمعالج الحدث LoadCompleted للتحقق من نجاح عملية التحميل أم لا.

يتطلب نمط العمليات الغير المتزامنة هذا أن يكون هناك إمكانية لتعطيل العملية التي يتم تنفيذها بشكل غير متزامن , والأداة PictureBox بالفعل تحقق هذا المتطلب, حيث أنها تحتوي على الإجراء CancelAsync , عند استدعاء الإجراء CancelAsync سيتم إرسال طلب لإيقاف عملية التحميل التي يتم تنفيذها حالياً , وعند إيقاف العملية سيتم عندها إطلاق الحدث LoadCompleted.

مميزات هذا النمط أو لنموذج

هذا النمط أخذ عدة أشكال استنادًا إلى درجة تعقيد السيناريو الذي تنفذه كل فئة, أبسط فئة قد تحتوي على إجراء وحيد MethodNameAsync وحدث موافق لها MethodNameCompleted , الفئات الأكثر تعقيدًا قد تحتوي على أكثر من إجراء غير متزامن كل إجراء يمتلك الحدث الموافق له , يمكن أيضا أن يكون له في المقابل إجراء MethodName يتم تنفيذه بشكل متزامن.يمكن لهذه الفئات أيضًا أن تدعم إلغاء تنفيذ العملية الحالية Cancellation , تقرير حالة التقدم Report Progressing والنتائج المتراكمة عن كل عملية غير متزامنة.

إجراء غير متزامن يمكن أيضًا أن يدعم استدعاءات متتالية ومتداخلة بحيث لا يتم تنفيذ الاستدعاء الثاني للإجراء حتى ينتهي الاستدعاء الأول , وبهذه الحال يستطيع كودك استدعاء الإجراء أكثر من مرة قبل أن ينتهي من العملية الحالية . ولتنفيذ هذه الميزة قد يحتاج تطبيقك إلى تتبع تنفيذ كل عملية حتى تنتهي.

أمثلة:

تعتبر الفئتان PictureBox و SoundPlayer أبسط تمثيل لهذا النمط, بينما تمثيل الفئتان BackgroundWorker و WebClient لهذا النمط يعتبر أكثر تعقيدًا من سابقتيهما , المثال التالي يعتبر تجريد لفئة وهمية تنفذ هذا النمط:

VB Code

كود :
Public Class AsyncExample
' Synchronous methods.
Public Function Method1(ByVal param As String) As Integer
Public Sub Method2(ByVal param As Double)

' Asynchronous methods.
Overloads Public Sub Method1Async(ByVal param As String)
Overloads Public Sub Method1Async(ByVal param As String, ByVal userState As Object)
Public Event Method1Completed As Method1CompletedEventHandler

Overloads Public Sub Method2Async(ByVal param As Double)
Overloads Public Sub Method2Async(ByVal param As Double, ByVal userState As Object)
Public Event Method2Completed As Method2CompletedEventHandler

Public Sub CancelAsync(ByVal userState As Object)

Public ReadOnly Property IsBusy () As Boolean

' Class implementation not shown.
End Class
C# Code


كود :
public class AsyncExample
{
// Synchronous methods.
public int Method1(string param);
public void Method2(double param);

// Asynchronous methods.
public void Method1Async(string param);
public void Method1Async(string param, object userState);
public event Method1CompletedEventHandler Method1Completed;

public void Method2Async(double param);
public void Method2Async(double param, object userState);
public event Method2CompletedEventHandler Method2Completed;

public void CancelAsync(object userState);

public bool IsBusy { get; }

// Class implementation not shown.
}
هذه الفئة تحتوي على إجراءين كلاهما يدعم التنفيذ المتزامن والغير متزامن , الإجراءات المتزامنة كأي إجراءات أخرى سيتم تنفيذها على نفس المسار للكود الذي قام باستدعائها مما يعني أنا إذا كان هذا الإجراء يستغرق وقتًا للتنفيذ فإنه سيكون هناك تأخر في الاستجابة قبل إرجاع النتائج, بينما الإجراءات الغير متزامنة ستبدأ العمل في مسار مستقل وبذلك تسمح لمسار الكود الذي قام باستدعائها بمتابعة العمل بينما يتم تنفيذ هذا الإجراء في الخلفية.

إعادة تحميل الإجراءات الغير متزامنة Overloading

عادة هناك نسختان من كل إجراء غير متزامن نسخة يتم استدعاؤها مرة واحدة فقط, ونسخة أخرى يمكن استدعاؤها أكثر من مرة (استدعاء متعدد) تمتلك معامل زائد userState هذه النسخة كما ذكرنا سابقًا تجعل من الممكن استدعاء الإجراء أكثر من مرة دون انتظار انتهاء الإجراء من القيام بالعملية الحالية, هذه النسخة يتم استدعاؤها بالشكل التالي:

كود :
Method1Async(string param, object userState)
أما النسخة الأولى والتي لا يمكن استدعاؤها مرة أخرى قبل انتهائها من العملية الحالية فإنه عند محاولة استدعائها قبل انتهائها سيتم إطلاق الاستثناء InvalidOperationException , ويكون استدعاؤها بالشكل التالي:

كود :
Method1Async(string param)
الوسيط userState في النسخة التي تقبل استدعائها أكثر من مرة , تمكنك من تحديد الاستدعاء للإجراء من خلال توفير قيمة فريدة GUID أو مفتاح مختلط HashCode , وعند انتهاء تنفيذ العملية يمكن للحدث MethodNameCompleted تحديد أي استدعاء تم الانتهاء منه.

تتبع العمليات التي في الانتظار.

إذا كنت تستخدم إجراء غير متزامن يعتمد على الاستدعاء المتعدد فإنك تحتاج إلى تتبع كل استدعاء بحيث كل استدعاء يتميز عن غيره بقيمة فريدة TaskID كما ذكرنا سابقًا إما GUID أو HashCode عند كل استدعاء للإجراء عليك القيام بتوليد تلك القيمة وإضافتها إلى HashTable حيث كل استدعاء يقابله ال userState الخاص به, عند انتهاء الاستدعاء الحالي يتم حذف ال userState والاستدعاء الخاص به من ال HashTable.

يمكنك الإطلاع على المثال التالي من MSDN لتتضح لك الصورة أكثر.
http://msdn.microsoft.com/en-us/library/9hk12d4y.aspx

وقريبًا جدًا سأضع مثال آخر يطبق نفس ال Pattern.

بالتوفيق
}}}
تم الشكر بواسطة:
#2
عندما نتحدث عن Event-based Asynchronous Pattern فإننا سنتحدث بالتأكيد عن مجموعة من الفئات والأنواع التي تم تصميمها خصيصاً لهذا ال Pattern والتي نجدها ضمن مجالي الأسماء System.Threading و System.ComponentModel وهي بالتحديد:

AsyncOperation
AsyncOperationManager
AsyncCompletedEventArgs
PogressChangedEventArgs

وبعض ال Delegate المعرفة سابقًا ضمن الدوت نت مثلل AsyncCallBack و SendOrPostCallBack

عندما يتم مناداة إجراء غير متزامن MethodNameAsync فإنه عادة يتم إنشاء نسخة من الفئة AsyncOperation والتي تعمل كوسيط بين ال Component أو الفئة التي تقوم ببنائها والبيئة التي ستعمل عليها سواء كان تطبيق Windows Forms أو تطبيق WPF أو تطبيق ASP.NET, أهم خصائص هذه الفئة هو الخاصية UserSuppliedState والتي يمكن من خلالها التمييز بين كل عملية, وأيضاً أهم إجراءين في هذه الفئة هما Post و PosOperationCompleted حيث تستخدم Post لإطلاق الحدث MethodNameProgressChanged بينما تستخدم PostOperationCompleted لإنهاء العملية وإطلاق الحدث MethodNameCompleted ,

والطريقة الشرعية الوحيدة لإنشاء نسخة من الفئة AsyncOperation هي استخدام فئة أخرى مساعدة وهي AsyncOperationManager واستخدام الإجراء المشترك CreateOperation بحيث يتم تمرير كائن من نوع Object وهو يمثل القيمة التي تميز كل AsyncOperation عن مثيلتها وستصبح هذه القيمة هي نفسها قيمة الخاصية UserSuppliedState.

المثال التالي عبارة عن Component بسيط يطبق هذا ال Pattern وقد أسميته AsynchronousStreamCopy ومن اسمه يتض أنه يقوم بنسخ Stream داخل Stream آخر , وهذا ال Component مفيد أحياناً عندما تقوم بنسخ ملفات ذات حجم كبير مع ملاحظة انه يمكن استخدامه مع أي فئة اخرى مشتقة من الفئة IO.Stream , و هو يحتوي إجراء واحد غير متزامن وهو CopyStreamAsync وهو إجراء Overloaded بحيث يتم في الأصل تمرير ثلاث وسيطات وهي ال Stream المصدر والذي سيتم نسخه و ال Stream الوجهة الذي سيتم النسخ إليه ووسيط ثالث يمثل القيمة الفريدة التي ستمرر للإجراء AsyncOperationManager.CreateOperation , والنسخة الثانية من هذا الإجراء تقوم فقط باستقبال ال Streams التي سيتم العمل عليها دون الحاجة لتمرير وسيط ثالث وفي هذه الحالة لن تتمكن من القيام بأكثر من استدعاء واحد للإجراء CopyStreamAsync وإذا قمت بذلك سيتم إطلاق الاستثناء InvalidOperationException. باللإضافة إلى الإجراء CancelAsync والذي يقوم بإيقاف أي عملية متزامنة تتم حالياً وهو أيضًا إجراء Overloaded بحيث يقبل وسيط واحد userState من نوع Object وهو نفسه الذي تم تمريره للإجراء CopyStreamAsync, ويعبر عن نفس العملية,

ملاخظة: إذا لم تقم بتمرير الوسيط الثالث للإجراء CopyStreamAsync فمن المفترض عند استدعاء الإجراء CancelAsync ألا تمرّر أي وسيط وإلا سيحدث استثناء.

بالنسبة لكيفية نسخ ال Streams فسيتم الاعتماد على الإجراءات الغير متزامنة للفئة Stream مثل BeginRead و EndRead و Beginwrite وهو ما سيوضحه المثال.

أيضًا تم إنشاء Delegates خاصة بالأحداث StreamCopyProgressChanged و StreamCopyCompleted

هذا الكود الخاص بالفئة StreamCopyProgressChangedEventArgs


كود :
Imports System.IO
Imports System.ComponentModel

Public Class StreamCopyProgressChangedEventArgs
Inherits ProgressChangedEventArgs

Private _bytesCopied As Long

Public Sub New(ByVal bytesCopied As Long _
, ByVal userState As Object _
, ByVal percentage As Integer)
MyBase.New(percentage, userState)
Me._bytesCopied = bytesCopied
End Sub


Public ReadOnly Property BytesCopied() As Long
Get
Return Me._bytesCopied
End Get
End Property

End Class
قمت ايضًا بعمل فءة اخرى مساعدة وهي StreamContainer والتي ستحمل معلومات حول ال Streams وال AsyncOperation المرتبطة بها من أجل تمريرها للإجراءات BeginRead و BeginWrite.


كود :
Imports System.IO
Imports System.ComponentModel

Public NotInheritable Class StreamsContainer
Implements IStreamContainer

Private _Position As Integer
Private _BytesRead As Long
Private _source As Stream = Nothing
Private _destination As Stream = Nothing
Private _operation As AsyncOperation
Private _buffer() As Byte

Public Sub New(ByVal sourceStream As Stream, ByVal destinationStream As Stream _
, ByVal asyncOperation As AsyncOperation _
, ByVal bufferSize As Integer)
If sourceStream Is Nothing OrElse sourceStream.Equals(Stream.Null) Then
Throw New ArgumentNullException("sourceStream")
End If
If destinationStream Is Nothing OrElse sourceStream.Equals(Stream.Null) Then
Throw New ArgumentNullException("destinationStream")
End If
Me._operation = asyncOperation
Me._source = sourceStream
Me._destination = destinationStream
Me._buffer = New Byte(BufferSize) {}
Me.ValidateInternal()
End Sub
Public ReadOnly Property SourceStream() As Stream Implements IStreamContainer.SourceStream
Get
Return _source
End Get
End Property

Public ReadOnly Property DestinationStream() As Stream Implements IStreamContainer.DestinationStream
Get
Return _destination
End Get
End Property

Public Overrides Function ToString() As String
Return String.Format( _
"Source stream length in bytes: {0}, Destination stream length in bytes: {1}" _
, Me._source.Length, Me._destination.Length)
End Function

Private Sub ValidateInternal()
If Not _source.CanRead Then
Throw New IOException("Source stream is Write-only")
End If
If _source.Length = 0 Then
Throw New Exception("Source stream is empty.")
End If
If Not _destination.CanWrite Then
Throw New IOException("Destination stream is Read-only")
End If
End Sub

Public ReadOnly Property Operation() As AsyncOperation
Get
Return _operation
End Get
End Property

Public ReadOnly Property Buffer() As Byte()
Get
Return Me._buffer
End Get
End Property

Public Property Position() As Long
Get
Return _Position
End Get
Set(ByVal value As Long)
_Position = value
End Set
End Property

End Class
وهذه هي Delegates الخاصة بالأخداث:


كود :
Imports System.ComponentModel

Public Delegate Sub StreamCopyCompletedEventHandler(ByVal e As AsyncCompletedEventArgs)

Public Delegate Sub StreamCopyProgressChangedEventHandler(ByVal e As StreamCopyProgressChangedEventArgs)
}}}
تم الشكر بواسطة:
#3
وهذا هو كود ال Component الخاص بنا:



كود :
Imports System.IO
Imports System.Threading
Imports System.ComponentModel
Imports System.Collections.Specialized

Public Class AsynchronousStreamCopy
Inherits Component

#Region "Events"

Public Custom Event StreamCopyProgressChanged As StreamCopyProgressChangedEventHandler
AddHandler(ByVal value As StreamCopyProgressChangedEventHandler)
SyncLock ChangedLock
Me.Events.AddHandler(ChangedLock, value)
End SyncLock
End AddHandler

RemoveHandler(ByVal value As StreamCopyProgressChangedEventHandler)
SyncLock ChangedLock
Me.Events.RemoveHandler(ChangedLock, value)
End SyncLock
End RemoveHandler

RaiseEvent(ByVal e As StreamCopyProgressChangedEventArgs)
Dim handler As StreamCopyProgressChangedEventHandler = Nothing
SyncLock ChangedLock
handler = Me.Events(ChangedLock)
End SyncLock
If handler IsNot Nothing Then
handler(e)
End If
End RaiseEvent

End Event
Public Custom Event StreamCopyCompleted As StreamCopyCompletedEventHandler
AddHandler(ByVal value As StreamCopyCompletedEventHandler)
SyncLock CompletedLock
Me.Events.AddHandler(CompletedLock, value)
End SyncLock
End AddHandler

RemoveHandler(ByVal value As StreamCopyCompletedEventHandler)
SyncLock CompletedLock
Me.Events.RemoveHandler(CompletedLock, value)
End SyncLock
End RemoveHandler

RaiseEvent(ByVal e As AsyncCompletedEventArgs)
Dim handler As StreamCopyCompletedEventHandler = Nothing
SyncLock CompletedLock
handler = Me.Events(CompletedLock)
End SyncLock
If handler IsNot Nothing Then
handler.Invoke(e)
End If
End RaiseEvent
End Event

#End Region

#Region "Delegates"

Private Delegate Sub Worker(ByVal container As StreamsContainer)

#End Region

#Region "Fields"

Private OnCopyCompletedDelegate As SendOrPostCallback
Private OnCopyProgressChangedDelegate As SendOrPostCallback
Protected TasksHash As New HybridDictionary
Private Const _defaultId As String = "Default"
Private ReadOnly _buffersize As Integer = &H1000
Private components As IContainer = Nothing
Private ReadOnly ChangedLock As New Object()
Private ReadOnly CompletedLock As New Object()

#End Region

#Region "Constructors"

Public Sub New()
MyBase.New()
InitializeDelegates()
End Sub

Public Sub New(ByVal container As IContainer)
Me.New()
container.Add(Me)
End Sub

Public Sub New(ByVal container As IContainer, ByVal buferrsize As Integer)
Me.New(container)
Me._buffersize = _buffersize
End Sub

Public Sub New(ByVal container As IContainer, ByVal buffersize As Integer, ByVal sc As SynchronizationContext)
Me.New(container, buffersize)
AsyncOperationManager.SynchronizationContext = sc
End Sub

Public Sub New(ByVal sc As SynchronizationContext)
Me.New()
AsyncOperationManager.SynchronizationContext = sc
End Sub

Public Sub New(ByVal buffersize As Integer)
Me.New()
Me._buffersize = buffersize
End Sub


Prvate Sub InitializeDelegates()
OnCopyCompletedDelegate = New SendOrPostCallback(AddressOf CopyCompleted)
OnCopyProgressChangedDelegate = New SendOrPostCallback(AddressOf CopyProgressChanged)
End Sub

#End Region

#Region "Methods"

Public Overloads Sub CopyStreamAsync(ByVal sourceStream As Stream, ByVal destinationStream As Stream)
CopyStreamAsync(sourceStream, destinationStream, _defaultId)
End Sub

Public Overloads Sub CopyStreamAsync(ByVal sourceStream As Stream _
, ByVal destination As Stream _
, ByVal taskId As Object)

Dim operation As AsyncOperation = AsyncOperationManager.CreateOperation(taskId)

SyncLock TasksHash.SyncRoot
If TasksHash.Contains(taskId) Then
If taskId Is _defaultId Then
Throw New InvalidOperationException("Worker is busy")
End If
Throw New Exception("Task already exists")
End If
TasksHash(TaskId) = operation
End SyncLock

Dim container As New StreamsContainer(sourceStream, destination, operation, _buffersize)

Dim worker As New Worker(AddressOf StartCopy)
worker.BeginInvoke(container, Nothing, Nothing)

End Sub

Private Function TaskExists(ByVal taskId As Object) As Boolean
Return Not TasksHash(taskId) Is Nothing
End Function

Private Sub StartCopy(ByVal container As StreamsContainer)

If TaskExists(container.Operation.UserSuppliedState) Then
Try
Dim source = container.SourceStream
source.BeginRead(container.Buffer, 0, container.Buffer.Length _
, New AsyncCallback(AddressOf ReadEnd), container)
Catch ex As Exception
Me.CompleteCopy(container, ex, False)
End Try
Else
Throw New InvalidOperationException("Task doesn't exist.")
End If
End Sub


Private Sub ReadEnd(ByVal results As IAsyncResult)

Dim container = CType(results.AsyncState, StreamsContainer)
Try
Dim read = container.SourceStream.EndRead(results)
container.Position += read
If TaskExists(container.Operation.UserSuppliedState) Then
If read = 0 Then
Me.CompleteCopy(container, Nothing, False)
Else
container.DestinationStream.BeginWrite( _
container.Buffer, 0, container.Buffer.Length, _
New AsyncCallback(AddressOf Me.WriteEnd), container)
End If
Else
Me.CompleteCopy(container, Nothing, True)
End If
Catch ex As Exception
Me.CompleteCopy(container, ex, False)
End Try

End Sub


Private Sub WriteEnd(ByVal results As IAsyncResult)
Dim container = CType(results.AsyncState, StreamsContainer)
If TaskExists(container.Operation.UserSuppliedState) Then
Try
container.DestinationStream.EndWrite(results)
Dim percentage As Integer = _
container.Position / container.SourceStream.Length * 100
Dim e As New StreamCopyProgressChangedEventArgs(container.Position _
, container.Operation.UserSuppliedState, percentage)
container.Operation.Post(OnCopyProgressChangedDelegate, e)
container.SourceStream.BeginRead(container.Buffer, _
0, container.Buffer.Length, _
New AsyncCallback(AddressOf Me.ReadEnd), container)

Catch ex As Exception
Me.CompleteCopy(container, ex, False)
End Try
Else
Me.CompleteCopy(container, Nothing, True)
End If

End Sub


Private Sub CompleteCopy(ByVal container As StreamsContainer _
, ByVal exc As Exception _
, ByVal canceled As Boolean)

SyncLock TasksHash.SyncRoot
If TaskExists(container.Operation.UserSuppliedState) Then
TasksHash.Remove(container.Operation.UserSuppliedState)
End If
End SyncLock

Dim e As New AsyncCompletedEventArgs(exc, _
canceled, container.Operation.UserSuppliedState)

container.Operation.PostOperationCompleted(OnCopyCompletedDelegate, e)

End Sub


Private Sub CopyCompleted(ByVal e As AsyncCompletedEventArgs)
OnCopyProgressCompleted(e)
End Sub

Private Sub CopyProgressChanged(ByVal e As StreamCopyProgressChangedEventArgs)
OnCopyProgressChanged(e)
End Sub

Protected Overridable Sub OnCopyProgressChanged(ByVal e As StreamCopyProgressChangedEventArgs)
RaiseEvent StreamCopyProgressChanged(e)
End Sub

Protected Overridable Sub OnCopyProgressCompleted(ByVal e As AsyncCompletedEventArgs)
RaiseEvent StreamCopyCompleted(e)
End Sub

Public Overloads Sub CancelAsync()
CancelAsync(_defaultId)
End Sub

Public Overloads Sub CancelAsync(ByVal userState As Object)
If userstate Is Nothing Then
Throw New ArgumentNullException("userState")
End If
If Not TaskExists(userstate) Then
Throw New ArgumentException("userState doesn't exist.")
End If
SyncLock TasksHash
TasksHash.Remove(userstate)
End SyncLock
End Sub

Protected Overrides Sub Dispose(ByVal disposing As Boolean)
If disposing Then
If Not components Is Nothing Then
components.Dispose()
End If
End If
MyBase.Dispose(disposing)
End Sub

#End Region

#Region "Properties"

Public ReadOnly Property BufferSize() As Integer
Get
Return _buffersize
End Get
End Property

#End Region

End Class
المثال التالي يوضح كيفي استخدام ال Component السابق للقيام بنسخ عدة ملفات بشكل متتالي الواحد تلو الآخر:

كود :
Imports StreamCopyAsync
Imports System.ComponentModel
Imports System.Windows
Imports System.IO
Imports System.Threading

Public Class Form1

Dim mre As ManualResetEvent

Private Sub _AsyncCopy_StreamCopyCompleted(ByVal e As AsyncCompletedEventArgs) Handles _AsyncCopy.StreamCopyCompleted
If e.Error IsNot Nothing Then
MessageBox.Show(e.Error.Message)
Me.lblStatus.Text = "Copy was not successful."
ElseIf e.Cancelled Then
Me.lblStatus.Text = "Copy canceled."
Else
Me.lblStatus.Text = "Copy completed successfully."
End If
If Not mre.SafeWaitHandle.IsClosed Then
mre.Set()
End If
End Sub

Private Sub _AsyncCopy_StreamCopyProgressChanged(ByVal e As StreamCopyProgressChangedEventArgs) Handles _AsyncCopy.StreamCopyProgressChanged
Me.ProgressBar1.Value = e.ProgressPercentage
Me.lblStatus.Text = e.BytesCopied & " bytes copied. Total progress: " & e.ProgressPercentage & " %"
End Sub

Private Sub BtnStart_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnstart.Click
ThreadPool.QueueUserWorkItem(AddressOf StartCopy)
End Sub

Private Sub BtnCancel_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btncancel.Click
_AsyncCopy.CancelAsync()
mre.Reset();
mre.Dispose()
End Sub

Sub StartCopy()

Dim Sources As IEnumerable(Of FileStream) = _
My.Computer.FileSystem.GetFiles("D:") _
.Select(Function(x) File.OpenRead(x))

Dim destination As IEnumerable(Of FileStream) = _
My.Computer.FileSystem.GetFiles("D:") _
.Select(Function(x) File.OpenWrite(x.Replace("D:\", "D:\Copy of ")))

mre = New Semaphore(False)

Using SourceEnum = Sources.GetEnumerator, _
DestnationEnum = destination.GetEnumerator

While SourceEnum.MoveNext AndAlso DestnationEnum.MoveNext
Using CurrentSource = SourceEnum.Current, _
CurrentDest = DestnationEnum.Current

_AsyncCopy.CopyStreamAsync(SourceEnum.Current, DestnationEnum.Current)

mre.Dispose()
End Using
End While
End Using
sema = Nothing
End Sub

End Class
طبعاً يمكنكم استخدام الأداة على تطبيق WPF أو حتى ASP.NET فقط كل ماعليك هو تعريف نسخة من ال Component السابق واستخدامه بالشكل المناسب لك.
}}}
تم الشكر بواسطة:


المواضيع المحتمل أن تكون متشابهة .
الموضوع : الكاتب الردود : المشاهدات : آخر رد
  نظرة سريعة في الجديد في Visual Studio 2008 فيما يتعلق ببيئة التطوير RaggiTech 0 1,880 08-10-12, 05:45 PM
آخر رد: RaggiTech
  نظرة على جديد الفيجوال بيسك 9 - الجزء الأول RaggiTech 0 2,091 08-10-12, 05:38 PM
آخر رد: RaggiTech

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


يقوم بقرائة الموضوع: بالاضافة الى ( 1 ) ضيف كريم