What is the best technique to run a thread at set periodic intervals

EDN Admin

Well-known member
Joined
Aug 7, 2010
Messages
12,794
Location
In the Machine
Hello,
Please look at the comments at the beginning of each class. Any advice that can be provided on the best method to trigger a periodic process would be appreciated. Note, the period is rather small (100ms), and the process being triggered could
occasionally take more than 100ms (in which case, the next thread trigger should be delayed ... two threads shoud never overlap). Also, the class will never be instantiated more than once.
Thanks in advance,
Gary
<p style="margin:0in 0in 0pt <a rel="nofollow <span style="font-family:"Arial","sans-serif"; color:black; font-size:12pt Gary L. Pratt, P.E.<br/>
Manager of Application Development - GE4GE<br/>




<div style="color:Black;background-color:White; <pre>
<span style="color:Blue; Class Version1
<span style="color:Green; this version uses thread.sleep(), which I understand is not a good choice for small
<span style="color:Green; time periods (such as 100ms). This version also seems to use more CPU time and does
<span style="color:Green; not perform well
<span style="color:Blue; Sub main()
<span style="color:Blue; Dim ConnectedServer <span style="color:Blue; As iHistorian_SDK.Server
ConnectedServer = <span style="color:Blue; New iHistorian_SDK.Server
ConnectedServer.Connect()
<span style="color:Blue; Dim MyData <span style="color:Blue; As iHistorian_SDK.Data
MyData = ConnectedServer.Data
<span style="color:Blue; Dim MyRecordsetD <span style="color:Blue; As iHistorian_SDK.DataRecordset
MyRecordsetD = MyData.NewRecordset
<span style="color:Blue; Dim MyValue <span style="color:Blue; As iHistorian_SDK.DataValue
<span style="color:Blue; For Sample = 0 <span style="color:Blue; To 999
<span style="color:Green; note, the instructions in this loop might ocasionally take more than 100ms to execute
MyValue = MyRecordsetD.Add(<span style="color:#A31515; "Point1", Now())
MyValue.Value = Math.Sin(2.0 * Math.PI * 10.0 * Sample)
MyValue.DataQuality = iHistorian_SDK.ihDataQuality.Good
MyValue = MyRecordsetD.Add(<span style="color:#A31515; "POoint2", Now())
MyValue.Value = Math.Cos(2.0 * Math.PI * 10.0 * Sample)
MyValue.DataQuality = iHistorian_SDK.ihDataQuality.Good
MyRecordsetD.WriteRecordset()
MyRecordsetD.ClearRecordset()
Thread.Sleep(100)
<span style="color:Blue; Next
MyRecordsetD = <span style="color:Blue; Nothing
MyValue = <span style="color:Blue; Nothing
ConnectedServer.Disconnect()
<span style="color:Blue; End <span style="color:Blue; Sub
<span style="color:Blue; End <span style="color:Blue; Class
<span style="color:Blue; Class Version2
<span style="color:Green; This version uses waitone instead of sleep. Ive read this might be better?
<span style="color:Blue; Shared Pause <span style="color:Blue; As <span style="color:Blue; New AutoResetEvent(<span style="color:Blue; False)
<span style="color:Blue; Sub main()
<span style="color:Blue; Dim ConnectedServer <span style="color:Blue; As iHistorian_SDK.Server
ConnectedServer = <span style="color:Blue; New iHistorian_SDK.Server
ConnectedServer.Connect()
<span style="color:Blue; Dim MyData <span style="color:Blue; As iHistorian_SDK.Data
MyData = ConnectedServer.Data
<span style="color:Blue; Dim MyRecordsetD <span style="color:Blue; As iHistorian_SDK.DataRecordset
MyRecordsetD = MyData.NewRecordset
<span style="color:Blue; Dim MyValue <span style="color:Blue; As iHistorian_SDK.DataValue
<span style="color:Blue; For Sample = 0 <span style="color:Blue; To 999
<span style="color:Green; note, the instructions in this loop might ocasionally take more than 100ms to execute
MyValue = MyRecordsetD.Add(<span style="color:#A31515; "Point1", Now())
MyValue.Value = Math.Sin(2.0 * Math.PI * 10.0 * Sample)
MyValue.DataQuality = iHistorian_SDK.ihDataQuality.Good
MyValue = MyRecordsetD.Add(<span style="color:#A31515; "POoint2", Now())
MyValue.Value = Math.Cos(2.0 * Math.PI * 10.0 * Sample)
MyValue.DataQuality = iHistorian_SDK.ihDataQuality.Good
MyRecordsetD.WriteRecordset()
MyRecordsetD.ClearRecordset()
<span style="color:Blue; If (Pause.WaitOne(100)) <span style="color:Blue; Then : <span style="color:Blue; Exit <span style="color:Blue; For : <span style="color:Blue; End <span style="color:Blue; If
<span style="color:Blue; Next
MyRecordsetD = <span style="color:Blue; Nothing
MyValue = <span style="color:Blue; Nothing
ConnectedServer.Disconnect()
<span style="color:Blue; End <span style="color:Blue; Sub
<span style="color:Blue; End <span style="color:Blue; Class
<span style="color:Blue; Class Version3
<span style="color:Green; this version uses the parent thread to time and release the child thread.
<span style="color:Green; running this version sometimes causes both CPUs to go to 100% utilization
<span style="color:Green; and the performance drops significantly. (Process Task Manager uses on
<span style="color:Green; CPU 100%).
<span style="color:Blue; Shared NextSample <span style="color:Blue; As <span style="color:Blue; New AutoResetEvent(<span style="color:Blue; False)
<span style="color:Blue; Sub main()
<span style="color:Blue; Dim WorkerThread <span style="color:Blue; As <span style="color:Blue; New Thread(<span style="color:Blue; AddressOf WorkerThreadSub)
WorkerThread.Start()
<span style="color:Blue; While WorkerThread.IsAlive
Thread.Sleep(100) <span style="color:Green; OR could use Pause.WaitOne(100) like version 2
NextSample.<span style="color:Blue; Set()
<span style="color:Blue; End <span style="color:Blue; While
<span style="color:Blue; End <span style="color:Blue; Sub

<span style="color:Blue; Sub WorkerThreadSub()
<span style="color:Blue; Dim ConnectedServer <span style="color:Blue; As iHistorian_SDK.Server
ConnectedServer = <span style="color:Blue; New iHistorian_SDK.Server
ConnectedServer.Connect()
<span style="color:Blue; Dim MyData <span style="color:Blue; As iHistorian_SDK.Data
MyData = ConnectedServer.Data
<span style="color:Blue; Dim MyRecordsetD <span style="color:Blue; As iHistorian_SDK.DataRecordset
MyRecordsetD = MyData.NewRecordset
<span style="color:Blue; Dim MyValue <span style="color:Blue; As iHistorian_SDK.DataValue
<span style="color:Blue; For Sample = 0 <span style="color:Blue; To 999
<span style="color:Green; note, the instructions in this loop might ocasionally take more than 100ms to execute
MyValue = MyRecordsetD.Add(<span style="color:#A31515; "Point1", Now())
MyValue.Value = Math.Sin(2.0 * Math.PI * 10.0 * Sample)
MyValue.DataQuality = iHistorian_SDK.ihDataQuality.Good
MyValue = MyRecordsetD.Add(<span style="color:#A31515; "POoint2", Now())
MyValue.Value = Math.Cos(2.0 * Math.PI * 10.0 * Sample)
MyValue.DataQuality = iHistorian_SDK.ihDataQuality.Good
MyRecordsetD.WriteRecordset()
MyRecordsetD.ClearRecordset()
NextSample.WaitOne()
<span style="color:Blue; Next
MyRecordsetD = <span style="color:Blue; Nothing
MyValue = <span style="color:Blue; Nothing
ConnectedServer.Disconnect()
<span style="color:Blue; End <span style="color:Blue; Sub
<span style="color:Blue; End <span style="color:Blue; Class
<span style="color:Blue; Class Version4
<span style="color:Green; this version doesnt work. The data in MyRecordsetD seems to be corrupted when it
<span style="color:Green; is used in the TransferRowOfDataSub thread. Why wont MyRecordsetD share between threads
<span style="color:Green; and how does one know when it is safe to share and not safe to share? Also, there is no
<span style="color:Green; protection in here to avoid two or more instances of TransferRowOfDataSub thread from
<span style="color:Green; running simultaneously, and I suspect there is a risk of one more thread being launched
<span style="color:Green; after Sample > 999 but before the parent has a chance to execute DataReaderTimer.Dispose()
<span style="color:Blue; Shared TransferRowOfDataSubDone <span style="color:Blue; As <span style="color:Blue; New AutoResetEvent(<span style="color:Blue; False)
<span style="color:Blue; Shared MyRecordsetD <span style="color:Blue; As iHistorian_SDK.DataRecordset
<span style="color:Blue; Shared Sample <span style="color:Blue; As UInt16 = 0
<span style="color:Blue; Sub main()
<span style="color:Blue; Dim ConnectedServer <span style="color:Blue; As iHistorian_SDK.Server
ConnectedServer = <span style="color:Blue; New iHistorian_SDK.Server
ConnectedServer.Connect()
<span style="color:Blue; Dim MyData <span style="color:Blue; As iHistorian_SDK.Data
MyData = ConnectedServer.Data
MyRecordsetD = MyData.NewRecordset
<span style="color:Blue; Dim TransferRowOfData <span style="color:Blue; As TimerCallback = <span style="color:Blue; New TimerCallback(<span style="color:Blue; AddressOf TransferRowOfDataSub)
<span style="color:Blue; Dim DataReaderTimer <span style="color:Blue; As <span style="color:Blue; New Timer(TransferRowOfData, vbNull, 1000, 100)
TransferRowOfDataSubDone.WaitOne()
DataReaderTimer.Dispose()
MyRecordsetD = <span style="color:Blue; Nothing
ConnectedServer.Disconnect()
<span style="color:Blue; End <span style="color:Blue; Sub

<span style="color:Blue; Sub TransferRowOfDataSub()
<span style="color:Green; note, the instructions below might ocasionally take more than 100ms to execute
<span style="color:Blue; Dim MyValue <span style="color:Blue; As iHistorian_SDK.DataValue
MyValue = MyRecordsetD.Add(<span style="color:#A31515; "Point1", Now())
MyValue.Value = Math.Sin(2.0 * Math.PI * 10.0 * Sample)
MyValue.DataQuality = iHistorian_SDK.ihDataQuality.Good
MyValue = MyRecordsetD.Add(<span style="color:#A31515; "POoint2", Now())
MyValue.Value = Math.Cos(2.0 * Math.PI * 10.0 * Sample)
MyValue.DataQuality = iHistorian_SDK.ihDataQuality.Good
MyRecordsetD.WriteRecordset()
MyRecordsetD.ClearRecordset()
Sample = Sample + 1
<span style="color:Blue; If Sample > 999 <span style="color:Blue; Then
TransferRowOfDataSubDone.<span style="color:Blue; Set()
MyValue = <span style="color:Blue; Nothing
<span style="color:Blue; End <span style="color:Blue; If
<span style="color:Blue; End <span style="color:Blue; Sub
<span style="color:Blue; End <span style="color:Blue; Class
<span style="color:Blue; Class Version5
<span style="color:Green; this version addresses the termination issue in version 4, but it seems dangerous
<span style="color:Green; for a thread to terminate the timer that launched the thread. Is there a better
<span style="color:Green; way to terminate the timer?
<span style="color:Blue; Shared DataReaderTimer <span style="color:Blue; As Timer
<span style="color:Blue; Shared TransferRowOfData <span style="color:Blue; As TimerCallback
<span style="color:Blue; Shared TransferRowOfDataSubDone <span style="color:Blue; As <span style="color:Blue; New AutoResetEvent(<span style="color:Blue; False)
<span style="color:Blue; Shared MyRecordsetD <span style="color:Blue; As iHistorian_SDK.DataRecordset
<span style="color:Blue; Shared Sample <span style="color:Blue; As UInt16 = 0
<span style="color:Blue; Sub main()
<span style="color:Blue; Dim ConnectedServer <span style="color:Blue; As iHistorian_SDK.Server
ConnectedServer = <span style="color:Blue; New iHistorian_SDK.Server
ConnectedServer.Connect()
<span style="color:Blue; Dim MyData <span style="color:Blue; As iHistorian_SDK.Data
MyData = ConnectedServer.Data
MyRecordsetD = MyData.NewRecordset
TransferRowOfData = <span style="color:Blue; New TimerCallback(<span style="color:Blue; AddressOf TransferRowOfDataSub)
DataReaderTimer = <span style="color:Blue; New Timer(TransferRowOfData, vbNull, 1000, 100)
TransferRowOfDataSubDone.WaitOne()
MyRecordsetD = <span style="color:Blue; Nothing
ConnectedServer.Disconnect()
<span style="color:Blue; End <span style="color:Blue; Sub

<span style="color:Blue; Sub TransferRowOfDataSub()
<span style="color:Green; note, the instructions below might ocasionally take more than 100ms to execute
<span style="color:Blue; Dim MyValue <span style="color:Blue; As iHistorian_SDK.DataValue
MyValue = MyRecordsetD.Add(<span style="color:#A31515; "Point1", Now())
MyValue.Value = Math.Sin(2.0 * Math.PI * 10.0 * Sample)
MyValue.DataQuality = iHistorian_SDK.ihDataQuality.Good
MyValue = MyRecordsetD.Add(<span style="color:#A31515; "POoint2", Now())
MyValue.Value = Math.Cos(2.0 * Math.PI * 10.0 * Sample)
MyValue.DataQuality = iHistorian_SDK.ihDataQuality.Good
MyRecordsetD.WriteRecordset()
MyRecordsetD.ClearRecordset()
Sample = Sample + 1
<span style="color:Blue; If Sample > 999 <span style="color:Blue; Then
TransferRowOfDataSubDone.<span style="color:Blue; Set()
DataReaderTimer.Dispose()
MyValue = <span style="color:Blue; Nothing
<span style="color:Blue; End <span style="color:Blue; If
<span style="color:Blue; End <span style="color:Blue; Sub
<span style="color:Blue; End <span style="color:Blue; Class

[/code]


View the full article
 
Back
Top