Advertisement
PiToLoKo

Elektro's Custom BGW Implementation

May 14th, 2016
305
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
VB.NET 22.51 KB | None | 0 0
  1. ' -------------------
  2. ' Info of interest...
  3. ' -------------------
  4. '
  5. ' BackgroundWorker Component Overview:
  6. ' http://msdn.microsoft.com/en-us/library/8xs8549b%28v=vs.110%29.aspx
  7. '
  8. ' Walkthrough: Multithreading with the BackgroundWorker Component:
  9. ' http://msdn.microsoft.com/en-us/library/ywkkz4s1.aspx
  10. '
  11. ' How to: Use a Background Worker:
  12. ' https://msdn.microsoft.com/es-es/library/cc221403%28v=vs.95%29.aspx
  13. '
  14. ' How to: Implement a Form That Uses a Background Operation:
  15. ' http://msdn.microsoft.com/en-us/library/waw3xexc%28v=vs.110%29.aspx
  16.  
  17.  
  18. #Region " Usage Examples "
  19.  
  20. 'Public Class Form1
  21. '
  22. '    Friend MyWork As Worker
  23. '
  24. '    Private Sub Run_Click() Handles Button_Start.Click
  25. '        If (Me.MyWork IsNot Nothing) AndAlso (Me.MyWork.IsRunning) Then
  26. '            Me.MyWork.Cancel()
  27. '        End If
  28. '
  29. '        Me.MyWork = New Worker
  30. '        Me.MyWork.RunAsync()
  31. '    End Sub
  32. '
  33. '    Private Sub Pause_Click() Handles Button_Pause.Click
  34. '        Me.MyWork.Pause()
  35. '    End Sub
  36. '
  37. '    Private Sub Resume_Click() Handles Button_Resume.Click
  38. '        Me.MyWork.Resume()
  39. '    End Sub
  40. '
  41. '    Private Sub Cancel_Click() Handles Button_Cancel.Click
  42. '        Me.MyWork.Cancel()
  43. '    End Sub
  44. '
  45. 'End Class
  46.  
  47. #End Region
  48.  
  49. #Region " Option Statements "
  50.  
  51. Option Strict On
  52. Option Explicit On
  53. Option Infer Off
  54.  
  55. #End Region
  56.  
  57. #Region " Imports "
  58.  
  59. Imports System.ComponentModel
  60. Imports System.Diagnostics
  61. Imports System.Threading
  62.  
  63. #End Region
  64.  
  65. #Region " Worker "
  66.  
  67. Public NotInheritable Class Worker : Implements IDisposable
  68.  
  69. #Region " Private Fields "
  70.  
  71.     ''' ----------------------------------------------------------------------------------------------------
  72.     ''' <summary>
  73.     ''' The underliying <see cref="BackgroundWorker"/> instance.
  74.     ''' </summary>
  75.     ''' ----------------------------------------------------------------------------------------------------
  76.     Private WithEvents bgw As BackgroundWorker
  77.  
  78.     ''' ----------------------------------------------------------------------------------------------------
  79.     ''' <summary>
  80.     ''' A <see cref="ManualResetEvent"/> that serves to handle synchronous operations (Run, Cancel).
  81.     ''' </summary>
  82.     ''' ----------------------------------------------------------------------------------------------------
  83.     Private ReadOnly mreSync As ManualResetEvent
  84.  
  85.     ''' ----------------------------------------------------------------------------------------------------
  86.     ''' <summary>
  87.     ''' A <see cref="ManualResetEvent"/> that serves to handle asynchronous operations (RunAsync, Pause, Resume, CancelAsync).
  88.     ''' </summary>
  89.     ''' ----------------------------------------------------------------------------------------------------
  90.     Private ReadOnly mreAsync As ManualResetEvent
  91.  
  92.     ''' ----------------------------------------------------------------------------------------------------
  93.     ''' <summary>
  94.     ''' Indicates whether <see cref="BackGroundworker"/> has been initiated in synchronous mode.
  95.     ''' </summary>
  96.     ''' ----------------------------------------------------------------------------------------------------
  97.     Private isRunSync As Boolean = False
  98.  
  99.     ''' ----------------------------------------------------------------------------------------------------
  100.     ''' <summary>
  101.     ''' Indicates whether a synchronous cancellation operation is requested.
  102.     ''' </summary>
  103.     ''' ----------------------------------------------------------------------------------------------------
  104.     Private isCancelSyncRequested As Boolean = False
  105.  
  106.     ''' ----------------------------------------------------------------------------------------------------
  107.     ''' <summary>
  108.     ''' Indicates whether a pause operation is requested.
  109.     ''' </summary>
  110.     ''' ----------------------------------------------------------------------------------------------------
  111.     Private isPauseRequested As Boolean = False
  112.  
  113. #End Region
  114.  
  115. #Region " Properties "
  116.  
  117.     ''' ----------------------------------------------------------------------------------------------------
  118.     ''' <summary>
  119.     ''' Gets a value indicating whether this instance is running.
  120.     ''' </summary>
  121.     ''' ----------------------------------------------------------------------------------------------------
  122.     ''' <value>
  123.     ''' <see langword="True"/> if this instance is running, otherwise, <see langword="False"/>.
  124.     ''' </value>
  125.     ''' ----------------------------------------------------------------------------------------------------
  126.     <EditorBrowsable(EditorBrowsableState.Always)>
  127.     Public ReadOnly Property IsRunning As Boolean
  128.         Get
  129.             Return (Me.bgw IsNot Nothing) AndAlso (Me.bgw.IsBusy)
  130.         End Get
  131.     End Property
  132.  
  133.     ''' ----------------------------------------------------------------------------------------------------
  134.     ''' <summary>
  135.     ''' Gets a value indicating whether this instance is paused.
  136.     ''' </summary>
  137.     ''' ----------------------------------------------------------------------------------------------------
  138.     ''' <value>
  139.     ''' <see langword="True"/> if this instance is paused; otherwise, <see langword="False"/>.
  140.     ''' </value>
  141.     ''' ----------------------------------------------------------------------------------------------------
  142.     <EditorBrowsable(EditorBrowsableState.Always)>
  143.     Public ReadOnly Property IsPaused As Boolean
  144.         Get
  145.             Return (Me.bgw IsNot Nothing) AndAlso (Me.isPausedB)
  146.         End Get
  147.     End Property
  148.     ''' ----------------------------------------------------------------------------------------------------
  149.     ''' <summary>
  150.     ''' (Backing Field)
  151.     ''' A value indicating whether this instance is paused.
  152.     ''' </summary>
  153.     ''' ----------------------------------------------------------------------------------------------------
  154.     Private isPausedB As Boolean = False
  155.  
  156.     ''' ----------------------------------------------------------------------------------------------------
  157.     ''' <summary>
  158.     ''' Gets a value indicating whether this instance is disposed.
  159.     ''' </summary>
  160.     ''' ----------------------------------------------------------------------------------------------------
  161.     ''' <value>
  162.     ''' <see langword="True"/> if this instance is disposed; otherwise, <see langword="False"/>.
  163.     ''' </value>
  164.     ''' ----------------------------------------------------------------------------------------------------
  165.     <EditorBrowsable(EditorBrowsableState.Always)>
  166.     Public ReadOnly Property IsDisposed As Boolean
  167.         <DebuggerStepThrough>
  168.         Get
  169.             Return (Me.bgw Is Nothing)
  170.         End Get
  171.     End Property
  172.  
  173. #End Region
  174.  
  175. #Region " Constructors "
  176.  
  177.     ''' ----------------------------------------------------------------------------------------------------
  178.     ''' <summary>
  179.     ''' Initializes a new instance of the <see cref="Worker"/> class.
  180.     ''' </summary>
  181.     ''' ----------------------------------------------------------------------------------------------------
  182.     <DebuggerNonUserCode>
  183.     Public Sub New()
  184.  
  185.         Me.bgw = New BackgroundWorker()
  186.  
  187.         Me.mreSync = New ManualResetEvent(initialState:=False)
  188.         Me.mreAsync = New ManualResetEvent(initialState:=True)
  189.  
  190.     End Sub
  191.  
  192. #End Region
  193.  
  194. #Region " Public Methods "
  195.  
  196.     ''' ----------------------------------------------------------------------------------------------------
  197.     ''' <summary>
  198.     ''' Starts this <see cref="Worker"/> and blocks the caller thread until the work is done.
  199.     ''' </summary>
  200.     ''' ----------------------------------------------------------------------------------------------------
  201.     ''' <exception cref="InvalidOperationException">
  202.     ''' This Worker is already running.
  203.     ''' </exception>
  204.     ''' ----------------------------------------------------------------------------------------------------
  205.     <DebuggerStepThrough>
  206.     Public Sub Run()
  207.  
  208.         If Not (Me.IsDisposed) AndAlso Not (Me.bgw.IsBusy) Then
  209.             Me.isRunSync = True
  210.             With Me.bgw
  211.                 .WorkerSupportsCancellation = False
  212.                 .WorkerReportsProgress = False
  213.                 .RunWorkerAsync()
  214.             End With
  215.             Me.mreSync.WaitOne()
  216.  
  217.         Else
  218.             Throw New InvalidOperationException("This Worker is already running.")
  219.  
  220.         End If
  221.  
  222.     End Sub
  223.  
  224.     ''' ----------------------------------------------------------------------------------------------------
  225.     ''' <summary>
  226.     ''' Asynchonouslly starts this <see cref="Worker"/>.
  227.     ''' </summary>
  228.     ''' ----------------------------------------------------------------------------------------------------
  229.     ''' <exception cref="InvalidOperationException">
  230.     ''' This Worker is already running.
  231.     ''' </exception>
  232.     ''' ----------------------------------------------------------------------------------------------------
  233.     <DebuggerStepThrough>
  234.     Public Sub RunAsync()
  235.  
  236.         If Not (Me.IsDisposed) AndAlso Not (Me.bgw.IsBusy) Then
  237.             With Me.bgw
  238.                 .WorkerSupportsCancellation = True
  239.                 .WorkerReportsProgress = True
  240.                 .RunWorkerAsync()
  241.             End With
  242.  
  243.         Else
  244.             Throw New InvalidOperationException("This Worker is already running.")
  245.  
  246.         End If
  247.  
  248.     End Sub
  249.  
  250.     ''' ----------------------------------------------------------------------------------------------------
  251.     ''' <summary>
  252.     ''' Pauses this <see cref="Worker"/>.
  253.     ''' </summary>
  254.     ''' ----------------------------------------------------------------------------------------------------
  255.     ''' <exception cref="InvalidOperationException">
  256.     ''' This Worker is not running.
  257.     ''' </exception>
  258.     ''' ----------------------------------------------------------------------------------------------------
  259.     <DebuggerStepThrough>
  260.     Public Sub Pause()
  261.  
  262.         If Not (Me.IsDisposed) AndAlso Not (Me.isPausedB) Then
  263.             Me.isPauseRequested = True
  264.             Me.isPausedB = True
  265.             Me.mreAsync.Reset()
  266.  
  267.         Else
  268.             Throw New InvalidOperationException("This Worker is not running.")
  269.  
  270.         End If
  271.  
  272.     End Sub
  273.  
  274.     ''' ----------------------------------------------------------------------------------------------------
  275.     ''' <summary>
  276.     ''' Resumes this <see cref="Worker"/>.
  277.     ''' </summary>
  278.     ''' ----------------------------------------------------------------------------------------------------
  279.     ''' <exception cref="InvalidOperationException">
  280.     ''' This Worker is not paused.
  281.     ''' </exception>
  282.     ''' ----------------------------------------------------------------------------------------------------
  283.     <DebuggerStepThrough>
  284.     Public Sub [Resume]()
  285.  
  286.         If Not (Me.IsDisposed) AndAlso (Me.isPausedB) Then
  287.             Me.isPausedB = False
  288.             Me.isPauseRequested = False
  289.             Me.mreAsync.Set()
  290.  
  291.         Else
  292.             Throw New InvalidOperationException("This Worker is not paused.")
  293.  
  294.         End If
  295.  
  296.     End Sub
  297.  
  298.     ''' ----------------------------------------------------------------------------------------------------
  299.     ''' <summary>
  300.     ''' Cancels this <see cref="Worker"/> and blocks the caller thread until the remaining work is done.
  301.     ''' </summary>
  302.     ''' ----------------------------------------------------------------------------------------------------
  303.     ''' <exception cref="InvalidOperationException">
  304.     ''' This Worker is not initialized.
  305.     ''' </exception>
  306.     ''' ----------------------------------------------------------------------------------------------------
  307.     <DebuggerStepThrough>
  308.     Public Sub Cancel()
  309.  
  310.         Me.isCancelSyncRequested = True
  311.         Me.CancelAsync()
  312.         Me.mreSync.WaitOne()
  313.         Me.isCancelSyncRequested = False
  314.  
  315.     End Sub
  316.  
  317.     ''' ----------------------------------------------------------------------------------------------------
  318.     ''' <summary>
  319.     ''' Asynchronouslly cancels this <see cref="Worker"/>.
  320.     ''' </summary>
  321.     ''' ----------------------------------------------------------------------------------------------------
  322.     ''' <exception cref="InvalidOperationException">
  323.     ''' This Worker is not initialized.
  324.     ''' </exception>
  325.     ''' ----------------------------------------------------------------------------------------------------
  326.     <DebuggerStepThrough>
  327.     Public Sub CancelAsync()
  328.  
  329.         If Not (Me.IsDisposed) AndAlso Not (Me.bgw.CancellationPending) AndAlso (Me.bgw.IsBusy) Then
  330.             Me.mreAsync.Set() ' Resume thread if it is paused.
  331.             Me.bgw.CancelAsync()
  332.  
  333.         Else
  334.             Throw New InvalidOperationException("This Worker is not initialized.")
  335.  
  336.         End If
  337.  
  338.     End Sub
  339.  
  340. #End Region
  341.  
  342. #Region " Private Methods "
  343.  
  344.     <DebuggerStepperBoundary>
  345.     Private Sub DoSomething()
  346.         Thread.Sleep(TimeSpan.FromSeconds(5))
  347.     End Sub
  348.  
  349. #End Region
  350.  
  351. #Region " Event-Handlers "
  352.  
  353.     ''' ----------------------------------------------------------------------------------------------------
  354.     ''' <summary>
  355.     ''' Handles the <see cref="BackgroundWorker.DoWork"/> event of the <see cref="bgw"/> instance.
  356.     ''' </summary>
  357.     ''' ----------------------------------------------------------------------------------------------------
  358.     ''' <param name="sender">
  359.     ''' The source of the event.
  360.     ''' </param>
  361.     '''
  362.     ''' <param name="e">
  363.     ''' The <see cref="DoWorkEventArgs"/> instance containing the event data.
  364.     ''' </param>
  365.     ''' ----------------------------------------------------------------------------------------------------
  366.     <DebuggerStepperBoundary>
  367.     Private Sub DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs) _
  368.     Handles bgw.DoWork
  369.  
  370.         Dim iterations As Integer = 5
  371.         Dim progress As Integer
  372.  
  373.         Dim lock As Object = ""
  374.         SyncLock lock
  375.  
  376.             For i As Integer = 0 To (iterations - 1)
  377.  
  378.                 If (Me.bgw.CancellationPending) Then
  379.                     e.Cancel = True
  380.                     Exit For
  381.  
  382.                 Else
  383.                     If Me.isPauseRequested Then ' Pause this thread right here.
  384.                         Me.mreAsync.WaitOne(Timeout.Infinite)
  385.                     End If
  386.  
  387.                     Me.DoSomething()
  388.  
  389.                     If Me.bgw.WorkerReportsProgress Then
  390.                         progress = ((i + 1) * (100 \ iterations))
  391.                         Me.bgw.ReportProgress(progress)
  392.                     End If
  393.  
  394.                 End If
  395.  
  396.             Next i
  397.  
  398.         End SyncLock
  399.  
  400.         If (Me.bgw.WorkerReportsProgress) AndAlso Not (Me.bgw.CancellationPending) AndAlso (progress < 100) Then
  401.             Me.bgw.ReportProgress(100)
  402.         End If
  403.  
  404.         If (Me.isRunSync) OrElse (Me.isCancelSyncRequested) Then
  405.             Me.mreSync.Set()
  406.         End If
  407.  
  408.     End Sub
  409.  
  410.     ''' ----------------------------------------------------------------------------------------------------
  411.     ''' <summary>
  412.     ''' Handles the <see cref="BackgroundWorker.ProgressChanged"/> event of the <see cref="bgw"/> instance.
  413.     ''' </summary>
  414.     ''' ----------------------------------------------------------------------------------------------------
  415.     ''' <param name="sender">
  416.     ''' The source of the event.
  417.     ''' </param>
  418.     '''
  419.     ''' <param name="e">
  420.     ''' The <see cref="ProgressChangedEventArgs"/> instance containing the event data.
  421.     ''' </param>
  422.     ''' ----------------------------------------------------------------------------------------------------
  423.     <DebuggerStepperBoundary>
  424.     Private Sub ProgressChanged(ByVal sender As Object, ByVal e As ProgressChangedEventArgs) _
  425.     Handles bgw.ProgressChanged
  426.  
  427.         Debug.WriteLine(String.Format("Work Progress: {0:00.00}%", e.ProgressPercentage))
  428.  
  429.     End Sub
  430.  
  431.     ''' ----------------------------------------------------------------------------------------------------
  432.     ''' <summary>
  433.     ''' Handles the <see cref="BackgroundWorker.RunWorkerCompleted"/> event of the <see cref="bgw"/> instance.
  434.     ''' </summary>
  435.     ''' ----------------------------------------------------------------------------------------------------
  436.     ''' <param name="sender">
  437.     ''' The source of the event.
  438.     ''' </param>
  439.     '''
  440.     ''' <param name="e">
  441.     ''' The <see cref="RunWorkerCompletedEventArgs"/> instance containing the event data.
  442.     ''' </param>
  443.     ''' ----------------------------------------------------------------------------------------------------
  444.     <DebuggerStepperBoundary>
  445.     Private Sub Bgw_RunWorkerCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs) _
  446.     Handles bgw.RunWorkerCompleted
  447.  
  448.         If (e.Cancelled) Then
  449.             Debug.WriteLine("Work state: Cancelled")
  450.  
  451.         ElseIf (e.Error IsNot Nothing) Then
  452.             Debug.WriteLine("Work state: Error")
  453.  
  454.         Else
  455.             Debug.WriteLine("Work state: Success")
  456.  
  457.         End If
  458.  
  459.         Me.Dispose()
  460.  
  461.     End Sub
  462.  
  463. #End Region
  464.  
  465. #Region " IDisposable Implementation "
  466.  
  467.     ''' ----------------------------------------------------------------------------------------------------
  468.     ''' <summary>
  469.     ''' To detect redundant calls when disposing.
  470.     ''' </summary>
  471.     ''' ----------------------------------------------------------------------------------------------------
  472.     Private isDisposedB As Boolean
  473.  
  474.     ''' ----------------------------------------------------------------------------------------------------
  475.     ''' <summary>
  476.     ''' Releases all the resources used by this instance.
  477.     ''' </summary>
  478.     ''' ----------------------------------------------------------------------------------------------------
  479.     <DebuggerStepThrough>
  480.     Public Sub Dispose() Implements IDisposable.Dispose
  481.  
  482.         Me.Dispose(isDisposing:=True)
  483.         GC.SuppressFinalize(obj:=Me)
  484.  
  485.     End Sub
  486.  
  487.     ''' ----------------------------------------------------------------------------------------------------
  488.     ''' <summary>
  489.     ''' Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
  490.     ''' <para></para>
  491.     ''' Releases unmanaged and, optionally, managed resources.
  492.     ''' </summary>
  493.     ''' ----------------------------------------------------------------------------------------------------
  494.     ''' <param name="isDisposing">
  495.     ''' <see langword="True"/>  to release both managed and unmanaged resources;
  496.     ''' <see langword="False"/> to release only unmanaged resources.
  497.     ''' </param>
  498.     ''' ----------------------------------------------------------------------------------------------------
  499.     <DebuggerStepThrough>
  500.     Private Sub Dispose(ByVal isDisposing As Boolean)
  501.  
  502.         If (Not Me.isDisposedB) AndAlso (isDisposing) Then
  503.  
  504.             Me.bgw.Dispose()
  505.             Me.bgw = Nothing
  506.  
  507.             With Me.mreSync
  508.                 .SafeWaitHandle.Close()
  509.                 .SafeWaitHandle.Dispose()
  510.                 .Close()
  511.                 .Dispose()
  512.             End With
  513.  
  514.             With Me.mreAsync
  515.                 .SafeWaitHandle.Close()
  516.                 .SafeWaitHandle.Dispose()
  517.                 .Close()
  518.                 .Dispose()
  519.             End With
  520.  
  521.             Me.isPausedB = False
  522.             Me.isRunSync = False
  523.  
  524.         End If
  525.  
  526.         Me.isDisposedB = True
  527.  
  528.     End Sub
  529.  
  530. #End Region
  531.  
  532. #Region " Hidden Base Members "
  533.  
  534.     ''' ----------------------------------------------------------------------------------------------------
  535.     ''' <summary>
  536.     ''' Serves as a hash function for a particular type.
  537.     ''' </summary>
  538.     ''' ----------------------------------------------------------------------------------------------------
  539.     ''' <returns>
  540.     ''' A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.
  541.     ''' </returns>
  542.     ''' ----------------------------------------------------------------------------------------------------
  543.     <DebuggerNonUserCode>
  544.     <EditorBrowsable(EditorBrowsableState.Never)>
  545.     Public Shadows Function GetHashCode() As Integer
  546.         Return MyBase.GetHashCode
  547.     End Function
  548.  
  549.     ''' ----------------------------------------------------------------------------------------------------
  550.     ''' <summary>
  551.     ''' Gets the <see cref="System.Type"/> of the current instance.
  552.     ''' </summary>
  553.     ''' ----------------------------------------------------------------------------------------------------
  554.     ''' <returns>
  555.     ''' The exact runtime type of the current instance.
  556.     ''' </returns>
  557.     ''' ----------------------------------------------------------------------------------------------------
  558.     <EditorBrowsable(EditorBrowsableState.Never)>
  559.     <DebuggerNonUserCode>
  560.     Public Shadows Function [GetType]() As Type
  561.         Return MyBase.GetType
  562.     End Function
  563.  
  564.     ''' ----------------------------------------------------------------------------------------------------
  565.     ''' <summary>
  566.     ''' Determines whether the specified <see cref="Object"/> is equal to this instance.
  567.     ''' </summary>
  568.     ''' ----------------------------------------------------------------------------------------------------
  569.     ''' <param name="obj">
  570.     ''' Another object to compare to.
  571.     ''' </param>
  572.     ''' ----------------------------------------------------------------------------------------------------
  573.     ''' <returns>
  574.     ''' <see langword="True"/> if the specified <see cref="Object"/> is equal to this instance; otherwise, <see langword="False"/>.
  575.     ''' </returns>
  576.     ''' ----------------------------------------------------------------------------------------------------
  577.     <EditorBrowsable(EditorBrowsableState.Never)>
  578.     <DebuggerNonUserCode>
  579.     Public Shadows Function Equals(ByVal obj As Object) As Boolean
  580.         Return MyBase.Equals(obj)
  581.     End Function
  582.  
  583.     ''' ----------------------------------------------------------------------------------------------------
  584.     ''' <summary>
  585.     ''' Returns a string that represents the current object.
  586.     ''' </summary>
  587.     ''' ----------------------------------------------------------------------------------------------------
  588.     ''' <returns>
  589.     ''' A string that represents the current object.
  590.     ''' </returns>
  591.     ''' ----------------------------------------------------------------------------------------------------
  592.     <EditorBrowsable(EditorBrowsableState.Never)>
  593.     <DebuggerNonUserCode>
  594.     Public Shadows Function ToString() As String
  595.         Return MyBase.ToString
  596.     End Function
  597.  
  598. #End Region
  599.  
  600. End Class
  601.  
  602. #End Region
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement