|
-
Nov 6th, 2009, 05:42 AM
#1
Thread Starter
New Member
Calling a function on a different thread
Hi,
I have a couple of threads running. One is a trigger which picks up a system event, the other processes stuff. When the trigger picks up the event I want it to notify the processor thread to do stuff. The threads are both started within the same class and can access the thread-object of the other thread. How do I set up the function call though?
Thanks.
-
Nov 6th, 2009, 05:53 AM
#2
Hyperactive Member
Re: Calling a function on a different thread
You have to use Delegates.
Here they explain how they work:
http://www.developerfusion.com/artic...ates-in-vbnet/
I think you have to do something like this:
vb Code:
Friend NotInheritable Class Class1 Private Delegate Sub DelSub() Private _del As New DelSub(AddressOf DoWork) Private _thread As New System.Threading.Thread(AddressOf ThreadWork) Public Sub New() _thread.Start() End Sub Private Sub DoWork() 'I do a lot of work End Sub Private Sub ThreadWork() _del.Invoke() End Sub End Class
Last edited by gonzalioz; Nov 6th, 2009 at 05:57 AM.
-
Nov 6th, 2009, 06:01 AM
#3
Thread Starter
New Member
Re: Calling a function on a different thread
Thanks gonzalioz, I thought deligates might have something to do with it but you have made it much clearer than the other sites.
I have a question though. I have two threads in the class which are both running. How do I specify, from within the trigger thread, that _del should be invoked on the processor thread?
Also, will the trigger thread wait for the processor thread to finish processing _del before continuing? I would like it to just carry on waiting for another trigger event while the processor processes. Maybe I have to start a third triggerNotify thread which does the notifying on a different thread?
Last edited by metamind; Nov 6th, 2009 at 06:04 AM.
-
Nov 6th, 2009, 06:12 AM
#4
Re: Calling a function on a different thread
If you want something to be executed on a specific thread then you should look into the SynchronizationContext class - see posts #29 to #33 in this thread: http://www.vbforums.com/showthread.php?t=587341
Oh and as for the trigger thread waiting, no it will not wait. If a thread has no more work to do then it terminates, regardless of whether or not other threads that have been started from that thread are still doing work.
-
Nov 6th, 2009, 06:15 AM
#5
Hyperactive Member
Re: Calling a function on a different thread
Sorry I don't fully understand. Could you post your code? Or maybe the link that Chris posted already answered your question . Nvm then.
You could just start the thread everytime you have to process data:
vb Code:
Friend NotInheritable Class Class1 Private _threadTrigger As New System.Threading.Thread(AddressOf ThreadTriggerWork) Private _threadProcessData As New System.Threading.Thread(AddressOf ThreadProcessWork) Public Sub New() _threadTrigger.Start() End Sub Private Sub ThreadTriggerWork() _threadProcessData.Start() End Sub Private Sub ThreadProcessWork() 'I do a lot of work End Sub End Class
-
Nov 6th, 2009, 07:03 AM
#6
Thread Starter
New Member
Re: Calling a function on a different thread
Thanks for that. I am a bit confused though as to how to get the SynchronizationContext stuff to work.
A bit of background
I am trying to write a service that watches a directory. If files are added / changed then the changes need to be uploaded to a server.
I am using the FileSystemWatcher class to notify me of the changes. However, this is a little flakey. E.g. if you save an open word doc to the watched-directory, then a load of files are created in the directory at the same time. Also, the FileSystemWatcher makes the thread sleep until something happens. So, what I am doing is the watching thread (the trigger thread) calls the processing thread once something has changed and the processing thread will do its stuff while the trigger thread goes back to watching (sleeping).
The code (so far) is below. As you can see, I have set up the Deligate _thdStartProcessing which is what I want to call. I have _thcProcessorContext As SynchronizationContext which I set up in thProcessor_start using
_thcProcessorContext = SynchronizationContext.Current
It is the line in doStuff that I can't figure out:
_thcProcessorContext.Post(New SendOrPostCallback(AddressOf startProcessing), Nothing)
is what is in the code below but it doesn't like it. I'm not sure what should go here.
Code:
Imports System.ServiceProcess
Imports System.io
Imports System.Threading
Public Class Service1
Private WithEvents _fsw As FileSystemWatcher
Private Delegate Sub DelSub()
Private _thdStartProcessing As New DelSub(AddressOf startProcessing)
Private _baseDir As String = "C:\test\"
Private _watchedDir As String = _baseDir & "watched dir\"
Private _hFiles As New Hashtable
Private _thTrigger As Thread
Private _thProcessor As Thread
Private _thcProcessorContext As SynchronizationContext
Private _sw As StreamWriter
Private _processorFromTime As DateTime
Private _processorLength As Integer = 60 ' how long should the processor run in seconds
Private _currentlyProcessing As Boolean = False
Protected Overrides Sub OnStart(ByVal args() As String)
_sw = New StreamWriter(_baseDir & "test.txt")
_sw.AutoFlush = True
Dim tsTrigger As New ThreadStart(AddressOf Me.thTrigger_start)
_thTrigger = New Thread(tsTrigger)
_sw.WriteLine("starting trigger thread")
_thTrigger.Start()
_sw.WriteLine("trigger thread started")
Dim tsProcessor As New ThreadStart(AddressOf Me.thProcessor_start)
_thProcessor = New Thread(tsProcessor)
_sw.WriteLine("starting processor thread")
_thProcessor.Start()
_sw.WriteLine("processor thread started")
End Sub
Protected Sub thProcessor_start()
_thcProcessorContext = SynchronizationContext.Current
startProcessing()
End Sub
Public Sub startProcessing()
_processorFromTime = Now
If _currentlyProcessing = False Then
_currentlyProcessing = True
Do While Now.Subtract(_processorFromTime).TotalSeconds < _processorLength
System.Threading.Thread.Sleep(1000)
process()
Loop
End If
_currentlyProcessing = False
End Sub
Protected Sub process()
' do stuff
End Sub
' the trigger thread notifies the processor that a file has changed
Protected Sub thTrigger_start()
_sw.WriteLine("starting OnStart_thread")
_fsw = New FileSystemWatcher
_sw.WriteLine("v3")
_sw.WriteLine("1")
_fsw.Path = _watchedDir
_sw.WriteLine("2")
_fsw.IncludeSubdirectories = False
_sw.WriteLine("3")
Try
_sw.WriteLine("4")
While True
_sw.WriteLine("5")
_fsw.WaitForChanged(WatcherChangeTypes.All)
_sw.WriteLine("6")
End While
_sw.WriteLine("7")
Catch ex As Exception
_sw.WriteLine("error")
End Try
_sw.WriteLine("8")
End Sub
Protected Overrides Sub OnStop()
' shut down the threads
_sw.WriteLine("closing")
_sw.Close()
End Sub
Private Sub _fsw_Created(ByVal sender As Object, ByVal e As System.IO.FileSystemEventArgs) Handles _fsw.Created
doStuff(e.FullPath.Substring(_watchedDir.Length), "created")
'File.Copy(e.FullPath, _baseDir & "watched dir\processing\")
'File.Delete(e.FullPath)
End Sub
Private Sub _fsw_Changed(ByVal sender As Object, ByVal e As System.IO.FileSystemEventArgs) Handles _fsw.Changed
doStuff(e.FullPath.Substring(_watchedDir.Length), "changed")
End Sub
Private Sub doStuff(ByVal fn As String, ByVal source As String)
_thcProcessorContext.Post(New SendOrPostCallback(AddressOf startProcessing), Nothing)
End Sub
End Class
-
Nov 6th, 2009, 08:55 AM
#7
Re: Calling a function on a different thread
I would just do essentially what Gonzalioz suggested and kick off a new thread each time your FSW (filesystemwatcher) detects a change. I dont see the need for the FSW to be running on a worker thread as well, unless of course this "processing" thread is doing more than just waiting for the FSW to pass it things to do, but from your code it doesnt look like that.
So for example, your OnStart method would simply configure and start the FSW (not on a different thread) and would not start a new 'processing thread' or anything like that. Then your event handlers for the FSW.Changed and FSW.Created etc would create a new thread and pass in the file path that has been changed so that this new thread can process it. That make sense?
-
Nov 6th, 2009, 09:02 AM
#8
Re: Calling a function on a different thread
Objects don't run on any specific thread. I think people have got this idea because controls have an affinity for a specific thread but even controls don't run on any particular thread. You can access any object on any thread at any time; even controls. The only real restriction is that you can only access the handle of a control on the thread that created it. That's where the thread-affinity of controls comes from. Anything that doesn't require accessing a control's handle can be done on any thread you like.
As for other types, they don't even have the thread-affinity that controls do, so what thread they get created on is generally completely irrelevant. That is certainly the case for a FileSystemWatcher. The thread it's created on makes no difference whatsoever because every time it raises an even it will be raised on a different thread anyway. The FileSystemWatcher class uses the ThreadPool and will raise an event on whatever ThreadPool thread happens to be available at the time. For that reason, starting a new thread from a FileSystemWatcher's event handler is also pointless. Why create a new thread when you're already on a different thread to begin with?
-
Nov 6th, 2009, 09:18 AM
#9
Re: Calling a function on a different thread
For that reason, starting a new thread from a FileSystemWatcher's event handler is also pointless. Why create a new thread when you're already on a different thread to begin with?
To be honest I thought that might be the case but I havent worked with the FSW for a while and the OP said:
Also, the FileSystemWatcher makes the thread sleep until something happens.
and I foolishly believed him
-
Nov 6th, 2009, 11:26 AM
#10
Thread Starter
New Member
Re: Calling a function on a different thread
Yup. There are a few reasons I have gone round the houses with this. The FSW triggers multiple times in rapid succession in a number of different scenarios. It seems to fire the "created" event and sometimes the "changed" event - sometimes multiple times - for a new file. I did put in a load of filtering code to check whether the notifications were new or not but it got a bit ridiculous. If the user saves an open word document then it is fired for the all the temp files (the ones starting "~"). In fact it was so busy firing for the temp files that it missed the actual .doc.
Hence I decided that I would assume that I couldn't trust the exact details of the FSW firing and I would create a set of functions that would process the directory and assume nothing about what was changed but go back and look at the contents of the directory, copy over the files that have changed since it last polled and process them accordingly. Hence I was going to use the FSW as a wake call for the processor. On wake up the processor runs for 60 seconds polling the directory every second. If the processor is already working when it gets notification from the FSW then it just rests the clock.
Thinking about it, I guess the trigger thread can check whether the processor is processing and can reset the clock if it is. If not then it can start a new thread. This way the processor works for 60 seconds since the last change in the directory then stops.
I will give that a go.
Thanks for your help by the way. Much appreciated.
By the way, I was going to do the timer by having a "lastUpdated" variable that will be set to "now()" and the processor will stop polling and end at lastUpdated + 60 secs. What is the easiest way to make lastUpdated thread safe?
Last edited by metamind; Nov 6th, 2009 at 12:22 PM.
-
Nov 6th, 2009, 01:15 PM
#11
Re: Calling a function on a different thread
I dont know if you actually need to make a Date type variable thread safe - I remember asking about doing the same thing for a Boolean value recently but was told that there is no need as the Boolean can never be in an 'invalid' state, but that might be different with a Date object as obviously there is a lot more to a Date object than a Boolean.
The easiest way to make it thread safe is to use either a property or a method each time you want to work with the variable, rather than using the variable directly - then in this property/method you read or modify the value of the variable but wrap the code in a SyncLock block to stop more than one thread being in this code block at any one time. For example, something like this (sorry if its not perfect, its just off the top of my head):
vb Code:
'At class level
Private LastUpdated As Date
Private MyLockObject As New Object
Private Sub SetLastUpdated()
SyncLock MyLockObject
LastUpdated = Now
End SyncLock
End Sub
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|