65.9K
CodeProject is changing. Read more.
Home

Multithreaded timed-out operation

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.04/5 (10 votes)

Dec 17, 2004

2 min read

viewsIcon

46957

downloadIcon

174

Here is a class for making your multithreaded timed-out operation... without having to care about threads and timeout.

Introduction

Do you occasionally need multithreaded timed-out operations? Let me say, this is a very basic implementation, but is the best solution for trying many different methods for completing your operation before you choose the best one, and it lets you start from a small basic (but so easy to get) multithreaded stuff that you can grow or change as much as you like since this class is released under GNU LGPL compatible license (means I don't care anything).

I will refer to the example of a DNS resolving operation (scanner), but it can be any multithreaded timed-out operation.

Question: what should "atomically" do our operation? Here is an example (DNS resolving example, it's easy to write the atomic job):

   Private Function MyJob(IP as Object)As Object
         '(object you like)...What do you want to use/returnn?
         Dim IPString as String = IP
         '(with option strict off this is ok, otherwise ctype...)

         Try
            System.Net.Dns.Resolve(IPString)
            Return IPString
         Catch
            Return "NotResolved"
         End Try
   End Function

Now I want this to be multithreaded and don't want to wait for the DNS for more than 3 sec per thread.

This is where the class comes in handy.

To use the class, simply create a MOP MultiThreadedTimedOutOperation instance:

   Dim WithEvents MOP as MultiThreadedTimedOutOperation

Initialize MOP by giving a reference to our atomic job function, and optionally, the maximum number of threads and the timeout.

   MOP = New MultiThreadedTimedOutOperation(AddressOf MyJob,40,3000)

I want to scan my entire subnet. Imagine that, we have our subnet prefix stored in LocalSNPrefix (String) as ("1.2.3."), so add the 254 jobs just like this:

   Dim I as integer
   For I = 1 to 254
       MOP.QueueJob(LocalSNPrefix + I.ToString)
   Next

Now I want to start my operation:

   MOP.StartOperation

Now I can have implemented (not necessary) a function to catch performstepevent (to use with the progress bar). Anyway, you need to know when the operation is finished and of course get the results.

   Private Sub OnOpFinish()Handles MOP.OpFinished
      'Here you know the operation is stopped so
      Dim S as Stack = MOP.GetResults()
      'Do with the results what you like here...
      'in this example we can retrieve resolved ip

      Dim I as Integer
      For I = 1 to S.count
          Dim Result as string = S.Pop
          If Not Result.Equals("NotResolved")
              MessageBox.Show("ResolvedIP :" + Result)
      Next
   End sub

That's it. Anyway I don't suggest this class be used in any "release version" without "strong implementation" stuff like late binding objects, and often there would be no need to make sure that an atomic operation has finished if it is placed into a separate thread as the class does. In fact, often you will already have a function that supports timing out by its own.

And if you think you can do better, don't wait.

Here's the code, copy and paste it directly into your VB.NET project or download the file from the zip file.

Public Class MultiThreadedTimedOutOperation
    Public Delegate Function Job(ByVal InitState As Object) As Object
    Public Event PerformStep()
    Public Event OpFinished()
    Private JobStart As Job
    Private Results As New Stack
    Private TS As New Queue
    Private ActiveThreads As Integer
    Private ReadOnly MaxThreads As Integer = 40
    Private ReadOnly Waittime As Integer = 3000
    Public Sub New(ByVal Method As Job, Optional ByVal maxthreads _
           As Integer = 40, Optional ByVal Timeout As Integer = 3000)
        Me.MaxThreads = maxthreads
        Me.Waittime = Timeout
        JobStart = Method
    End Sub
    Public Sub QueueJob(ByVal UserJobStartState As Object)
        TS.Enqueue(UserJobStartState)
    End Sub
    Public Sub StartOperation()
        Dim t As New Threading.Thread(AddressOf MainJob)
        t.ApartmentState = Threading.ApartmentState.MTA
        t.Start()
    End Sub
    Public Function GetResults() As Stack
        Return Results
    End Function 
    Private Sub MainJob()
        While TS.Count > 0
            If ActiveThreads < MaxThreads Then
                Do1Job()
                RaiseEvent PerformStep()
            End If
            System.Threading.Thread.Sleep(Waittime / MaxThreads)
        End While
        RaiseEvent OpFinished()
    End Sub 
    Private Sub Do1Job()
        Dim t As New Threading.Thread(AddressOf TimingOutJob)
        t.Start()
    End Sub
    Private Sub TimingOutJob()
        ActiveThreads += 1
        Dim t As New Threading.Thread(AddressOf JobInvoke)
        t.Start()
        If Not t.Join(Waittime) Then t.Abort()
        ActiveThreads -= 1
    End Sub
    Private Sub JobInvoke()
        Dim ThisJob As String = TS.Dequeue
        Dim Result As Object = JobStart.Invoke(ThisJob, Nothing, Nothing)
        If Not Result Is Nothing Then Results.Push(Result)
    End Sub
End Class