So here is a VB.NET version of the above. Note that I have added in a parameter object to the class so you can pass that into the thread if need be. Also, the thread function makes a call to another function to do the actual work. So from this class that does the thread management, you can inherit another class that does the work, as well as add any other members you might want to update as the thread progresses.
Imports System.Threading
Public Class ThreadTask
Protected _firstRunComplete As Boolean = False
Public ReadOnly Property firstRunComplete() As Boolean
Get
Return _firstRunComplete
End Get
End Property
Protected _running As Boolean = False
Public ReadOnly Property Running() As Boolean
Get
Return _running
End Get
End Property
Protected _lastTaskSuccess As Boolean = True
Public ReadOnly Property LastTaskSuccess() As Boolean
Get
If (_lastFinishTime = Date.MinValue) Then
Throw New InvalidOperationException("The task has never completed.")
End If
Return _lastTaskSuccess
End Get
End Property
Protected _exceptionOccured As Exception = Nothing
Public ReadOnly Property ExceptionOccured() As Exception
Get
Return _exceptionOccured
End Get
End Property
Protected _lastStartTime As Date = Date.MinValue
Public ReadOnly Property LastStartTime() As Date
Get
If (_lastStartTime = Date.MinValue) Then
Throw New InvalidOperationException("The task has never started.")
End If
Return _lastStartTime
End Get
End Property
Protected _lastFinishTime As Date = Date.MinValue
Public ReadOnly Property LastFinishTime() As Date
Get
If (_lastFinishTime = Date.MinValue) Then
Throw New InvalidOperationException("The task has never completed.")
End If
Return _lastFinishTime
End Get
End Property
Protected _parameter As Object
Public ReadOnly Property Parameter() As Object
Get
Return _parameter
End Get
End Property
Public Sub RunTask(ByVal Parameter As Object)
SyncLock Me
If Not _running Then
Me._running = True
Me._lastStartTime = Date.Now
Dim t As Thread = New Thread(AddressOf Kickoff)
t.Start(Parameter)
Else
Throw New InvalidOperationException("The task is already running!")
End If
End SyncLock
End Sub
Protected Sub Kickoff(ByVal Parameter As Object)
Try
Me._parameter = Parameter
Dim ex As Exception = DoWork(Parameter)
If ex IsNot Nothing Then
Throw ex
End If
Me._lastTaskSuccess = True
Catch e As Exception
Me._lastTaskSuccess = False
Me._exceptionOccured = e
Finally
Me._running = False
Me._lastFinishTime = Date.Now
If Not Me._firstRunComplete Then Me._firstRunComplete = True
End Try
End Sub
Public Overridable Function DoWork(ByVal Parameter As Object) As Exception
Try
Thread.Sleep(8000)
Catch e As Exception
Return e
Finally
End Try
Return Nothing
End Function
End Class
Now derive something that actually does a useful job.
Last but not least, if you are running a thread people will want to know what is happening with it, so based on the original page I've made a Web User Control you can drop on the page. Just call it's Start method when you need to and boom shanka, all done.Public Class JobThread Inherits ThreadTask
Public Overrides Function DoWork(ByVal Parameter As Object) As Exception
Try
Dim Job As Integer = Parameter
Dim dc As New JobDataContext
dc.CommandTimeout = 10000
dc.ProcessJob(Job)
Catch e As Exception
Return e
End Try
Return Nothing
End Function
End Class
The control itself is designed to ensure only one thread is running. It also works between sessions as it runs the thread in the server cache under a unique global name. Useful if you close the wrong tab in your browser.
Markup:
<%@ Control Language="vb" AutoEventWireup="false" CodeBehind="JobWatcher.ascx.vb" Inherits="JobWatcher" %>
<script language="JavaScript">
var sURL = unescape(window.location.pathname);
function refresh() {
window.location.href = sURL;
}
</script>
<asp:UpdatePanel ID="upnlWatch" runat="server">
<ContentTemplate>
<asp:Timer ID="tmrRefresh" runat="server" Interval="3000" Enabled="false">
</asp:Timer>
<asp:Panel ID="pnlWorking" runat="server" BackColor="AliceBlue" BorderStyle="Solid"
BorderWidth="1" Visible="false">
<asp:Label ID="msgLabel" runat="server"></asp:Label>
<asp:Image ID="imgTrobber" runat="server" ImageUrl="~/Images/Throbber-mac.gif" Visible="false" />
</asp:Panel>
</ContentTemplate>
<Triggers>
<asp:AsyncPostBackTrigger ControlID="tmrRefresh" EventName="Tick" />
</Triggers>
</asp:UpdatePanel>
Code Behind:
Public Class ComparisonThread Inherits ThreadTask Imports System.ComponentModel
Partial Public Class JobWatcher
Inherits System.Web.UI.UserControl
Protected task As JobThread = Nothing
Protected _name As String = "JobWatcher"
Public Property Name() As String
Get
Return _name
End Get
Set(ByVal value As String)
_name = value
End Set
End Property
Public Sub Run(ByVal Parameter As Object)
If Cache(Me.Name) Is Nothing Then
task = New JobThread()
task.RunTask(Parameter)
Cache(Me.Name) = task
tmrRefresh.Enabled = True
pnlWorking.Visible = True
msgLabel.DataBind()
Else
msgLabel.Text = "There is already a Job running!"
End If
End Sub
Private Sub msgLabel_DataBinding(ByVal sender As Object, ByVal e As System.EventArgs) Handles msgLabel.DataBinding
If task IsNot Nothing Then
If task.Running Then
msgLabel.Text = _
"Job " & task.Parameter & " is running now.<br>" & _
"Time Started: " & task.LastStartTime.ToString() & "<br>" & _
"Time Elapsed: " & Math.Ceiling((Date.Now - task.LastStartTime).TotalSeconds) & " seconds."
Else
imgTrobber.Visible = False
tmrRefresh.Enabled = False
msgLabel.Text = "Job " & task.Parameter & " is <b>finished</b> now. <a href='javascript:refresh()'> (refresh page) </a><br>"
If task.LastTaskSuccess Then
msgLabel.Text &= "<font color='Green'>Job succeeded.</font>"
Else
msgLabel.Text &= "<font color='Red'>Job failed.</font>"
If task.ExceptionOccured IsNot Nothing Then
msgLabel.Text &= "<br>The exception was: " + task.ExceptionOccured.Message()
End If
End If
Cache.Remove(Me.Name)
End If
End If
End Sub
Private Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
task = CType(Cache(Me.Name), JobThread)
If task IsNot Nothing Then
If Not tmrRefresh.Enabled Then tmrRefresh.Enabled = True
If Not pnlWorking.Visible Then pnlWorking.Visible = True
msgLabel.DataBind()
Else
pnlWorking.Visible = False
End If
End Sub
End Class