Reputation: 35318
I have an application that runs a task which checks for a file in a directory and completes when a file has been added to the directory. Here's a simplified example:
Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim addedFile = Await Task.Factory.StartNew(New Func(Of FileInfo)(
Function()
Dim di = New DirectoryInfo("C:\_Temporary\Test")
Do While True
Dim files = di.GetFiles()
If (files.Count > 0) Then
Return files(0)
End If
Loop
End Function))
MsgBox(addedFile.FullName)
End Sub
I've left out superfluous details like cancellation tokens, etc.
The issue is that the CPU is holding steady around 12% when the code is running. Even if I comment out the body inside the while loop, it remains the same.
How can I create a looping mechanism, which is required for non-awaitable operations like waiting for a file to arrive in a directory, without using that much CPU?
Note: The question is not about the concrete case involving the file system; it's looping non-awaitable operations in general and the effect on the CPU.
The Windows event message loop, by contrast, takes up less than 1% -- e.g. if I look at the CPU usage of my app before I click "Button1" which runs the above code.
Upvotes: 0
Views: 1061
Reputation: 1511
You are polling. Polling is never a good idea for a number of reasons. Polling is very common in software, even in some well known commercial software that will remain nameless. It causes a host of problems from tying up the processor unnecessarily, as you already know, to preventing the processor from sleeping and causing your battery to drain faster than it would otherwise.
SOLUTION #1: The most desirable solution is to find a built-in watcher as someone else pointed out. Under the covers, the watcher is probably using either a specific interrupt mechanism or using a kernel timing routine to emulate an interrupt.
SOLUTION #2: If no such watcher exists, you can use a for/while loop with a sleep() to check every so many seconds: It checks, goes to sleep for an interval, and then checks again. The sleep() function will not use any processing time (not completely true but practically so).
Even with Solution #2, you can fall into some traps that will cause you to use more processing than you need to. Do a realistic analysis of how often you need to check your condition. A very common mistake is to set a very short polling period under the mistaken assumption that your application will react faster.
If your typical event occurs once every minute or two, and you need to react to the event within 5 seconds, then having a polling time of 10 msec doesn't buy any gain and hurts everyone's performance. In this situation, you can get away with polling every 2 seconds, almost three orders of magnitude less frequently than 10 ms.
Another thing that many aren't aware of is that the underlying resolution of timers, etc., in typical operating systems (e.g. Linux and Windows) is around 10 msec even though the data structures let you specify potentially into the microseconds.
Upvotes: 0
Reputation: 3785
So, it's better to check for your files once per second (however not using a While True
loop, always avoid them).
Dim timerDelegate As TimerCallback = AddressOf RepeatingFunction
Dim autoEvent As New AutoResetEvent(True)
Dim dt As New System.Threading.Timer(timerDelegate, autoEvent, 0, 1000)
MSDN: http://msdn.microsoft.com/it-it/library/system.threading.timer%28v=vs.110%29.aspx
Note: RepeatingFunction
has to be repleaced. Also the last parameter of Threading.Timer
constructor reprents the interval between ticks.
Upvotes: 0
Reputation: 3438
Use Filewatcher class to receive events when directory changes: http://msdn.microsoft.com/en-us/library/system.io.filesystemwatcher%28v=vs.110%29.aspx
' Create a new FileSystemWatcher and set its properties.
Dim watcher As New FileSystemWatcher()
watcher.Path = "C:\_Temporary\Test"
' Watch for changes in LastAccess and LastWrite times, and
' the renaming of files or directories.
watcher.NotifyFilter = (NotifyFilters.LastAccess Or NotifyFilters.LastWrite Or NotifyFilters.FileName Or NotifyFilters.DirectoryName)
' Only watch text files.
watcher.Filter = "*.txt"
' Add event handlers.
AddHandler watcher.Created, AddressOf OnChanged
' Define the event handlers.
Private Shared Sub OnChanged(source As Object, e As FileSystemEventArgs)
' Specify what is done when a file is changed, created, or deleted.
MsgBox(e.FullPath)
End Sub
Upvotes: 3