Need help using WaitHandles

  • Thread starter Thread starter Anthony Lopez
  • Start date Start date
A

Anthony Lopez

I attempted to test how waithandles and autoresetevents work with one
another in hopes to be able to use them in my application. However, I keep
hitting the wall.

Can some one look over the attached code and tell me what I am doing wrong.
I am getting the following errors:

C:\Development\Net\VB\TestThread\TestThread\bin>testthread
Waiting for all current task to complete
Task Id = 1 Sequence = 1
Task Run Id = 10 Task Mode = True

Task Id = 2 Sequence = 1
Task Run Id = 10 Task Mode = True

Task Id = 3 Sequence = 1
Task Run Id = 10 Task Mode = True


Unhandled Exception: System.NullReferenceException: Object reference not set
to
an instance of an object.
at System.Threading.WaitHandle.WaitMultiple(WaitHandle[] waitHandles,
Int32 m
illisecondsTimeout, Boolean exitContext, Boolean WaitAll)
at System.Threading.WaitHandle.WaitAll(WaitHandle[] waitHandles, Int32
millis
econdsTimeout, Boolean exitContext)
at System.Threading.WaitHandle.WaitAll(WaitHandle[] waitHandles)
at TestThread.TestThread.Main() in
C:\Development\Net\VB\TestThread\TestThrea
d\Module1.vb:line 48

What's odd about this is that I closely follows the example in Microsoft's
MSDN web site.

' File: TestThread.vb

Option Strict On
Option Explicit On

Imports System
Imports System.Console
Imports System.io
Imports System.Threading
Imports System.Threading.Thread



Module TestThread

Const MAXTASK As Integer = 5
Const MAXTHREAD As Integer = 25

<MTAThreadAttribute()> _
Sub Main()

Dim Tasks As New ArrayList
Dim RunTask As CTask

Dim iRunTaskId As Integer = 10
Dim bRunTaskMode As Boolean = True

Dim Sync(3) As AutoResetEvent

'Create an array used to define the task's Id and Sequence
Dim TaskDefn(,) As Integer = New Integer(,) {{1, 1}, {2, 1}, {3, 1},
{4, 2}, {5, 3}}

' Create all the task using the TaskDefn array
For i As Integer = 0 To MAXTASK - 1
Dim theTask As CTask = New CTask
theTask.Id = TaskDefn(i, 0)
theTask.Sequence = TaskDefn(i, 1)
Tasks.Add(theTask)
Next i

Dim iSeq As Integer = 1000
Dim iTask As Integer = 0

For Each RunTask In Tasks

If iSeq < RunTask.Sequence Then
Console.WriteLine("Waiting for all current task to
complete")
WaitHandle.WaitAll(Sync) ' <<< THIS IS THE ERROR LINE
Console.WriteLine("All task have completed")
iTask = 0
End If

iSeq = RunTask.Sequence

Sync(iTask) = New AutoResetEvent(False)

Dim TaskParam As New CTaskParam(RunTask, bRunTaskMode,
iRunTaskId, Sync(iTask))

Try
ThreadPool.QueueUserWorkItem(AddressOf cbStartProgram,
TaskParam)
Catch ex As Exception
Console.WriteLine("Main Error Message: " + vbCrLf +
ex.Message)
End Try

iTask += iTask

Next

Sleep(3000)
End Sub

Class CTaskParam
Public Task As CTask
Public Mode As Boolean
Public RunId As Integer
Public SignalWhenDone As AutoResetEvent

Sub New(ByVal TaskParam As CTask, ByVal ModeParam As Boolean, ByVal
RunIdParam As Integer, ByVal SignalParam As AutoResetEvent)
Me.Task = TaskParam
Me.Mode = ModeParam
Me.RunId = RunIdParam
Me.SignalWhenDone = SignalParam
End Sub


End Class

' This module represent an intermediary module used to create a thread
and process
' the parameters, eventually invokeing the actual object/module
Sub cbStartProgram(ByVal theTaskParam As Object)

' Get the arguments
Dim TaskParam As CTaskParam = CType(theTaskParam, CTaskParam)

Try
' Invoke the object
TaskParam.Task.Run(TaskParam.RunId, TaskParam.Mode)
Catch ex As Exception
Console.WriteLine("Start Program - Error Message: " + vbCrLf +
ex.Message)
Finally
TaskParam.SignalWhenDone.Set()
End Try

End Sub


End Module



' This Class represents any task that can be call from .Net
'
Class CTask

Dim iSequence As Integer
Dim iId As Integer


Property Sequence() As Integer
Get
Return iSequence
End Get
Set(ByVal Value As Integer)
iSequence = Value
End Set
End Property

Property Id() As Integer
Get
Return iId
End Get
Set(ByVal Value As Integer)
iId = Value
End Set
End Property


Sub Run(ByVal iTaskRunId As Int32, ByVal bTaskMode As Boolean)

Console.WriteLine("Task Id = " + Id.ToString + " Sequence = " +
Sequence.ToString)
Console.WriteLine("Task Run Id = " + iTaskRunId.ToString + " Task
Mode = " + bTaskMode.ToString)
Console.WriteLine()


Return
End Sub
End Class
 
Thanks for the help! I found my errors and have worked through them. I like
your suggestions of using a List.


I attempted to test how waithandles and autoresetevents work with one
another in hopes to be able to use them in my application. However, I
keep
hitting the wall.

Can some one look over the attached code and tell me what I am doing
wrong.
I am getting the following errors:

C:\Development\Net\VB\TestThread\TestThread\bin>testthread
Waiting for all current task to complete
Task Id = 1 Sequence = 1
Task Run Id = 10 Task Mode = True

Task Id = 2 Sequence = 1
Task Run Id = 10 Task Mode = True

Task Id = 3 Sequence = 1
Task Run Id = 10 Task Mode = True


Unhandled Exception: System.NullReferenceException: Object reference not
set to an instance of an object.
at System.Threading.WaitHandle.WaitMultiple(WaitHandle[] waitHandles,
Int32 millisecondsTimeout, Boolean exitContext, Boolean WaitAll)
at System.Threading.WaitHandle.WaitAll(WaitHandle[] waitHandles,
Int32 millisecondsTimeout, Boolean exitContext)
at System.Threading.WaitHandle.WaitAll(WaitHandle[] waitHandles)
at TestThread.TestThread.Main() in
C:\Development\Net\VB\TestThread\TestThread\Module1.vb:line 48

What's odd about this is that I closely follows the example in
Microsoft's
MSDN web site.

Which example? Do you have a link? The code you posted is fairly
awkward, so it would be unfortunate if there's an example similar in
structure on MSDN. I'd love to know what the actual article link is, if
that's the case.

As far as your specific error goes, it's fairly simple and really has
nothing at all to do with WaitHandles per se. You've declared an array of
four AutoResetEvent references named "Sync", but you have only initialized
three of the elements of the array when you call WaitHandle.WaitAll().

The most obvious, tempting fix is probably to fix the array declaration so
that it only creates an array of three elements (since that's the maximum
that you ever need), but that would result in your program deadlocking (of
sorts), as the next time you wind up waiting, you'll only have one
AutoResetEvent instance in the array that is going to wind up being set
thought the WaitAll() method will try to wait on all three. You also
still wind up with the bonus bug of failing to dispose properly of the
AutoResetEvent instances that you've created. And on top of that, if your
input data ever changes so that the sequence of tasks with the group
having the maximum count is not actually the first, you'll wind up with
the exact same NullReferenceException you're seeing now.

I'm not a big fan of the pattern you're working with here anyway. The
need to have subsequent elements in your array have values that are
intrinsicly dependent on elements that come previous in the array seems
wrong to me, and while I obviously don't know the exact design goal that
has led to this particular implementation, this strategy of pausing
execution while some group of tasks completes seems odd to me.

But, ignoring all that, it seems to me that a better way to approach this
particular implementation would be to use a List(Of AutoResetEvent)
instead of a simple array, adding a new instance with each task you add.
When you reach the "pausing" logic, after the WaitHandle.WaitAll() method
returns, you should enumerate the List(Of AutoResetEvent) instance,
disposing each instance in the list, and then calling the Clear() method
on the list before proceeding. (Note that if you make this change, you'll
no longer need the "iTask" variable at all...you can simply initialize
your "TaskParam" variable by using "New AutoResetEvent(False)" as the last
constructor argument, and then pass "TaskParam.SignalWhenDone" to the
List(Of AutoResetEvent).Add() method).

Hope that helps.

Pete
 
Back
Top