Results 1 to 4 of 4

Thread: [RESOLVED] Execution of For loop skips to end of SyncLock

  1. #1

    Thread Starter
    Hyperactive Member
    Join Date
    Apr 2007
    Location
    cobwebbed to PC
    Posts
    311

    Resolved [RESOLVED] Execution of For loop skips to end of SyncLock

    Hi Folks

    Im trying to make a timed scheduler in a threaded vb windows form application. With one job on the schedule everything works fine. But with two jobs on the schedule the main For loop inside the SyncLock() in the timer elapsed event never executes.
    When I step through the elapsed event sub execution gets to the For loop and then jumps to the end of the SyncLock() with out executing the For or anything after it within the locked block.
    I cant understand why this happens, maybe someone with fresh eyes could see whats going on?

    the relevant code is below.

    In Scheduler.vb the problems start at line 84
    VB Code:
    1. Imports System.Threading
    2. Imports System.Timers
    3.  
    4. ' Example creation of this singleton class:
    5. ' Dim MyScheduler As Scheduler = Scheduler.GetScheduler
    6.  
    7. Public NotInheritable Class Scheduler
    8.     Inherits System.Timers.Timer
    9.  
    10.     Private Class Job
    11.         ' Class for adding items to the schedule
    12.         Public Sub New(period As TimeSpan, contextKey As UInteger)
    13.             If period > TimeSpan.Zero Then
    14.                 JobKey = contextKey
    15.                 Start = Now
    16.                 Finish = Start + period
    17.             Else
    18.                 Throw New ArgumentOutOfRangeException
    19.             End If
    20.         End Sub
    21.         Public Period As TimeSpan
    22.         Public Start As DateTime
    23.         Public Finish As DateTime
    24.         Public JobKey As UInteger
    25.         Public ThreadId As Integer
    26.     End Class
    27.  
    28.     ' Dictionary to hold the scheduled jobs
    29.     Private Shared _schedule As New Dictionary(Of UInteger, Job)
    30.     ' List to hold references to the working threads
    31.     Private Shared _threads As New List(Of Threading.Thread)
    32.     ' The reference to the single scheduler instance
    33.     Private Shared _singleInstance As Scheduler
    34.     ' SyncLock objects
    35.     Private Shared _scheduleLock As New Object
    36.     Private Shared _threadsLock As New Object
    37.     Private Shared _instanceLock As New Object
    38.  
    39.     Private Sub New()
    40.         ' Setup and start the timer with an interval of 3 seconds
    41.         MyBase.New()
    42.         AddHandler MyBase.Elapsed, AddressOf Scheduler_Elapsed
    43.         MyBase.Interval = 3000
    44.         MyBase.Enabled = True
    45.         MyBase.Start()
    46.     End Sub
    47.  
    48.     Public Sub AddJob(period As TimeSpan, contextKey As UInteger)
    49.         SyncLock (_scheduleLock)
    50.             Dim count As UInteger = _schedule.Count
    51.             If Not count = 0 Then
    52.                 ' Adjust for zero-base
    53.                 count -= 1
    54.                 ' Create copy of keys
    55.                 Dim keys(count) As UInteger
    56.                 _schedule.Keys.CopyTo(keys, 0)
    57.                 ' Step through jobKeys on schedule
    58.                 For index As Integer = count To 0
    59.                     If _schedule.Item(keys(index)).JobKey = contextKey Then
    60.                         ' Remove any obsolete jobs
    61.                         _schedule.Remove(keys(index))
    62.                     End If
    63.                 Next
    64.             End If
    65.             ' Add new job
    66.             _schedule.Add(_schedule.Count, New Job(period, contextKey))
    67.         End SyncLock
    68.     End Sub
    69.  
    70.     Private Sub Scheduler_Elapsed(source As Object, e As ElapsedEventArgs) Handles MyBase.Elapsed
    71.         Dim check As DateTime = Now
    72.         Dim count As UInteger = _schedule.Count
    73.  
    74.         If count > 0 Then
    75.             SyncLock (_scheduleLock)
    76.                 ' Adjust for zero-base
    77.                 count -= 1
    78.                 Dim keys(count) As UInteger
    79.                 ' Copy all the keys from the shcdule to an array
    80.                 ' as the keys may not be in order and For...Each
    81.                 ' cannot be used when removing items
    82.                 _schedule.Keys.CopyTo(keys, 0)
    83.                 ' Step through each of the keys in the keys array
    84.                 For index As Integer = count To 0                       '<----- EXECUTION SKIPS FROM HERE TO END SYNCLOCK
    85.                     ' Check if any of the jobs on the schedule are due
    86.                     If DateTime.Compare(check, _schedule.Item(keys(index)).Finish) > 0 Then
    87.                         ' Create and start a new thread
    88.                         Dim jobThread As New Thread(AddressOf DoJob)
    89.                         ' Save the thread ID to the job object - this allows the job to know which thread is handling it
    90.                         _schedule.Item(keys(index)).ThreadId = jobThread.ManagedThreadId
    91.                         ' Add the new thread to the list of threads - to keep track of all the threads
    92.                         SyncLock (_threadsLock)
    93.                             _threads.Add(jobThread)
    94.                         End SyncLock
    95.                         ' Start the work thread, passing the job object - so that it knows which DUT context to use
    96.                         jobThread.Start(_schedule.Item(keys(index)))
    97.                         ' Remove the job from the schedule - to prevent the job from being started on subsequent elapsed events
    98.                         _schedule.Remove(keys(index))
    99.                     End If
    100.                 Next
    101.             End SyncLock
    102.         End If
    103.     End Sub
    104.  
    105.     Private Sub DoJob(ByVal job As Object)
    106.         ' Use the JobKey to get the context object for the relevant DUT
    107.         Dim dutContext As New DeviceUnderTest
    108.         dutContext = Form1.currentDuts.Item(job.JobKey)
    109.  
    110.         ' Get and run the next step in the program
    111.         ' NextProgramStep() just shows a message box at the moment
    112.         dutContext.NextProgramStep()
    113.  
    114.         ' After work, remove thread from list of threads
    115.         SyncLock (_threadsLock)
    116.             Dim count As UInteger = _threads.Count
    117.             If count > 0 Then
    118.                 ' Step through job threadIds of current threads
    119.                 For index As UInteger = count - 1 To 0
    120.                     ' Check if any threads have the same id as the current job
    121.                     If _threads(index).ManagedThreadId = job.ThreadId Then
    122.                         ' Remove any thread working on this job
    123.                         _threads.Remove(_threads.Item(index))
    124.                     End If
    125.                 Next
    126.             End If
    127.         End SyncLock
    128.     End Sub
    129.  
    130.     Public Shared ReadOnly Property GetScheduler As Scheduler
    131.         ' Ensures only one instance of the scheduler is created
    132.         Get
    133.             If _singleInstance Is Nothing Then
    134.                 SyncLock (_instanceLock)
    135.                     If _singleInstance Is Nothing Then
    136.                         _singleInstance = New Scheduler
    137.                     End If
    138.                 End SyncLock
    139.             End If
    140.             Return _singleInstance
    141.         End Get
    142.     End Property
    143.  
    144.     Overloads ReadOnly Property Interval As Double
    145.         ' Reads the timer interval property (set in Scheduler.New())
    146.         Get
    147.             Return MyBase.Interval
    148.         End Get
    149.     End Property
    150. End Class

    In Form1.vb
    VB Code:
    1. Imports System.Text
    2. Imports System.Linq
    3. Imports System.Net
    4. Imports System.Net.Sockets
    5. Imports System.Timers
    6.  
    7. Public Class Form1
    8.  
    9.     Dim sched As Scheduler
    10.     Public Shared currentDuts As New Dictionary(Of UInteger, DeviceUnderTest)
    11.     Dim newDutKey As UInteger
    12.  
    13.     Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
    14.         sched = Scheduler.GetScheduler
    15.         Dim testDUT1 As New DeviceUnderTest()
    16.         Dim testDUT2 As New DeviceUnderTest()
    17.         Dim newKey As UInteger = currentDuts.Count
    18.  
    19.         ' Add the 2 new DUT jobs to the schedule
    20.         ' (DUT class is empty class at the moment)
    21.         currentDuts.Add(newKey, testDUT1)
    22.         currentDuts.Add(newKey + 1, testDUT2)
    23.         sched.AddJob(TimeSpan.FromSeconds(4.0), newKey)
    24.         sched.AddJob(TimeSpan.FromSeconds(30.0), newKey + 1)
    25.     End Sub
    26. End Class
    Thanks

  2. #2
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    9,017

    Re: Execution of For loop skips to end of SyncLock

    Its not jumping to the end of the SyncLock, its just not looping. Look at what you have here:-
    vbnet Code:
    1. '
    2. For index As Integer = count To 0                       '<----- EXECUTION SKIPS FROM HERE TO END SYNCLOCK
    3.                     ' Check if any of the jobs on the schedule are due
    4.                     If DateTime.Compare(check, _schedule.Item(keys(index)).Finish) > 0 Then
    5.                         ' Create and start a new thread
    6.                         Dim jobThread As New Thread(AddressOf DoJob)
    7.                         ' Save the thread ID to the job object - this allows the job to know which thread is handling it
    8.                         _schedule.Item(keys(index)).ThreadId = jobThread.ManagedThreadId
    9.                         ' Add the new thread to the list of threads - to keep track of all the threads
    10.                         SyncLock (_threadsLock)
    11.                             _threads.Add(jobThread)
    12.                         End SyncLock
    13.                         ' Start the work thread, passing the job object - so that it knows which DUT context to use
    14.                         jobThread.Start(_schedule.Item(keys(index)))
    15.                         ' Remove the job from the schedule - to prevent the job from being started on subsequent elapsed events
    16.                         _schedule.Remove(keys(index))
    17.                     End If
    18.                 Next

    If count is ever greater than zero that loop will never be entered. That is probably what is happening.
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena

    Copy/move files using Windows Shell | I'm not wanted

    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. - jmcilhinney

    The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber

  3. #3

    Thread Starter
    Hyperactive Member
    Join Date
    Apr 2007
    Location
    cobwebbed to PC
    Posts
    311

    Re: Execution of For loop skips to end of SyncLock

    Ah, brilliant, thanks Niya.
    So I should change to For index As Integer = count to 0 Step -1 instead right?
    Thanks

  4. #4
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    9,017

    Re: Execution of For loop skips to end of SyncLock

    If you want the loop to go backwards then yes, use Step -1.
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena

    Copy/move files using Windows Shell | I'm not wanted

    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. - jmcilhinney

    The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width