I'd like to trigger a slow-running sub when particular emails arrive, but I don't want to block execution/freeze outlook.
I've written the sub as Public Async Sub... but part of the code would fall over if the sub was called twice in quick succession.
Is there any way to make a queue such that only one copy of my sub is running at any one time, while ensuring that it's still running in the background?
My solution at the moment is to have a public continue as Boolean with a waiting loop beforehand - but this is a bit on the ugly side!
Related
I have a BackgroundWorker routine that performs a lot of things and call ThreadSafe functions to update UI controls. In some points of that routine I call some subroutines to write log information into a SQL table, using the Parallel Library to make them in another Thread. It´s functioning like a charm.
The threaded LOG calls are like this, where Log_Compressed_File() is a subroutine:
Dim Log_Threaded as Thread = New Thread(Sub() Log_Compressed_File(Username, UserAreaCode, Filename))
Log_Threaded.IsBackground = False
Log_Threaded.Priority = ThreadPriority.Highest
Log_Threaded.SetApartmentState(Threading.ApartmentState.MTA)
Log_Threaded.Start
As shown above, each log call is made using 5 lines of code, defining the new thread itself and some important parameters.
My questions are:
1) can I produce a Sub to address these calls, trying to reduce the lines of the main routine?
Just to make it clear: I have more than 35 LOG calls within the main routine and, if each one would utilize 5 lines of code, I will have 175 lines, when I could have only the original 35 calls to a new subroutine that could address the new thread.
2) Can I call that subroutine from within the BackgroundWorker without to call a delegate function (ThreadSafe) like I use in UI updates?
The function like shown above can be performed within the BackgroundWorker without to a DELEGATE function (the LOG_COMPRESSED_FILE is a normal Sub, not a Delegate call).
Thanks in advance for any help!
Yes. Just write a method with the same three parameters as your Log_Compressed_File method and then put that code into that method.
Yes. Do you understand why you need to use a delegate at all? The point is that you cannot access the Handle of a control on any thread other than the one it was created on. Invoking a delegate ensures that code accessing the control's Handle is executed on the correct thread. If you're not accessing a control then there's no Handle so why would it matter what thread you execute the code on?
I currently have made an all in one peer to peer 'chat' program. It currently uses a timer to receive messages, and a client to send them to other people running the program. I would really like to make this whole system async so I stop getting complaints of the main executable freezing and such.
I have pasted all of my code(VB.net) here: http://pastebin.com/EcrtCgVc
Could someone assist me in making this system faster, async, or done better.
If you would like a link to the dropbox of the full source, I can provide this also.
I would suggest you to take a look at this example project:
Asynchronous Server Socket Example
http://msdn.microsoft.com/en-us/library/fx6588te%28v=vs.110%29.aspx
You can use it together with this example:
Asynchronous Client Socket Example
http://msdn.microsoft.com/en-us/library/bew39x2a%28v=vs.110%29.aspx
You may need to use async methods in your server code and you may use Delegate Sub to make your server run in another thread, so main thread(UI) will be still responsive.
http://msdn.microsoft.com/en-us/library/ms172879.aspx
Without looking at your code, I am also working with socket programming.
"Do not mix GUI code with business code."
Like jgauffin said, you should not combine code that run in the background with the GUI.
For example, if you're using a socket to accept data, or connections, you must run that method on a new thread.
Example...
Public Sub YourMainSub()
Dim threadOne As New System.Threading.Thread(AddressOf ListenCode)
threadOne.Start()
'Put other code here.....
End Sub
Public Sub ListenCode()
Dim newPerson = yourServer.Accept()
'Or
Dim newData = yourServer.Receive()
End Sub
Sorry if the code isn't exactly correct. I typed that by memory.
According to the answers to another question, the VB user interface cannot be updated if the thread that created it is busy: hence why big computational jobs usually have to go in a background task.
Here's what's mystifying then. I have the following code. It's called over in-process COM, like this
client calls showform()
client does loads of work, freezing up its own UI in the process
client finishes work, returns to updating its own UI
At step 2, the VB form is there but frozen - you can't interact with it. At step 3, the VB form becomes usable. But why is this? Surely the thread of execution has returned to the client? If the client is somehow handling events for the form, by what magic did it know what events to handle and where to send them?
<ComClass(ComClass1.ClassId, ComClass1.InterfaceId, ComClass1.EventsId)> _
Public Class ComClass1
Public Sub New()
MyBase.New()
End Sub
Private f1 As Form1
Public Sub showform()
f1 = New Form1()
f1.Show()
End Sub
End Class
The magic you speak of is the basis of Windows programming. My answer to your previous question explains why and how you can fix this. When making a COM call the client application just imports your procedure into their application. Whether they create a form by typing the code themselves or whether they create a form using code you have typed it doesn't change the nature of the object/owner relationship. A COM call to your showForm will still make f1 belong to the thread which made the call (client UI thread). The window handle for that window will still be the responsibility of the UI thread that created it (the client).
Creating a form only makes a mailbox (window handle). A UI thread is a mailman (message pumping loop). You aren't providing the client with a new mailman, just a new object with a mailbox. When the client program creates the window by making the COM call to your procedure it (the client UI thread) takes responsibility for delivering messages to the new form's mailbox (registers its window handle with the main UI thread). Their mailman still needs to send you messages to make your visual objects work. If he is busy trying to calculate pi to a trillion decimal places then your object freezes like everything else on his mail route.
check the form.load event. the form loads and runs code... that is where it freezes.
I am using an external DLL (pdfsharp) to open (then manipulate) lots of PDF files. I use:
Dim inputDocument = Pdf.IO.PdfReader.Open(PDFPath, IO.PdfDocumentOpenMode.ReadOnly)
Problem is - it seems to hang on certain, rare files. I don't seem to get any timeout - it just hangs for hours on this line. We read thousands of files with this code, always on tiny files, so I was thinking that a quick workaround might be to somehow timeout if the this method takes more than a second or two. But I don't see a simple way to do this. I am hoping to avoid spinning up a worker thread.
Any thoughts on how I might limit this threads allowed execution time, or is there a better (but simple) way?
The Open() call should not hang. Never. If you provide us with a file that causes Open() to hang, we can investigate this.
Does your program run on a server? Do you use a DEBUG build of PDFsharp? Maybe it's just a simple Debug.Assert() that is triggered, but noone can answer it. Using a RELEASE build would solve this.
We ended up working around this problem by creating an AbortableBackgroundWorker. I am not sure whose code this ended up being - but we found it online and sharing it here. In the rare case where one of the PDF's hangs the PdfSharp Open() call, we abort the background worker.
Public Class AbortableBackgroundWorker
Inherits BackgroundWorker
Private workerThread As Thread
Protected Overrides Sub OnDoWork(e As DoWorkEventArgs)
workerThread = Thread.CurrentThread
Try
MyBase.OnDoWork(e)
Catch generatedExceptionName As ThreadAbortException
e.Cancel = True
'We must set Cancel property to true!
'Prevents ThreadAbortException propagation
Thread.ResetAbort()
End Try
End Sub
Public Sub Abort()
If workerThread IsNot Nothing Then
workerThread.Abort()
workerThread = Nothing
End If
End Sub
End Class
I need to write a VB.Net 2008 applet to go through all the fixed-drives looking for some files. If I put the code in ButtonClick(), the UI freezes until the code is done:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
'TODO Find way to avoid freezing UI while scanning fixed drives
Dim drive As DriveInfo
Dim filelist As Collections.ObjectModel.ReadOnlyCollection(Of String)
Dim filepath As String
For Each drive In DriveInfo.GetDrives()
If drive.DriveType = DriveType.Fixed Then
filelist = My.Computer.FileSystem.GetFiles(drive.ToString, FileIO.SearchOption.SearchAllSubDirectories, "MyFiles.*")
For Each filepath In filelist
'Do stuff
Next filepath
End If
Next drive
End Sub
Google returned information on a BackGroundWorker control: Is this the right/way to solve this issue?
If not, what solution would you recommend, possibly with a really simple example?
FWIW, I read that Application.DoEvents() is a left-over from VBClassic and should be avoided.
Thank you.
The BackgroundWorker is a good way to solve your problem. Actually the documentation states this:
The BackgroundWorker class allows you to run an operation on a separate, dedicated thread. Time-consuming operations like downloads and database transactions can cause your user interface (UI) to seem as though it has stopped responding while they are running. When you want a responsive UI and you are faced with long delays associated with such operations, the BackgroundWorker class provides a convenient solution.
Put the process into a separate thread....
...using the BackgroundWorker component.
Disable UI components that should not be usable while the process workd.
Finished - the UI will still be responsive.
The key is to seperate the UI code from the actual functionality code.
The time-consuming functionality should run on a seperate thread. To achieve this, you can either:
Create and start a Thread object by
yourself
Create a Delegate and use
asynchronous invokation (using
BeginInvoke).
Create and start a BackgroundWorker.
As you mentioned, you should avoid Application.DoEvents(). A proper breakdown of the application's functionality will allow you to create an application which is designed to be responsive, rather than creating a non-responsive application with DoEvents "fixes" (which is costly, considered bad practice, and implies a bad design).
Since your method doesn't return a value and doesn't update the UI, the fastest solution might be creating a Delegate and using "fire and forget" asynchronous invokation:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Call New Action(AddressOf DrivesIteration).BeginInvoke(Nothing, Nothing)
End Sub
Private Sub DrivesIteration()
Dim drive As DriveInfo
Dim filelist As Collections.ObjectModel.ReadOnlyCollection(Of String)
Dim filepath As String
For Each drive In DriveInfo.GetDrives()
If drive.DriveType = DriveType.Fixed Then
filelist = My.Computer.FileSystem.GetFiles(drive.ToString, FileIO.SearchOption.SearchAllSubDirectories, "MyFiles.*")
For Each filepath In filelist
DoStuff(...)
Next
End If
Next
End Sub
BTW, For..Next blocks no longer have to end with "Next (something)", it is obsolete - VB now infers the (something) by itself, so there is no need to state it explicitly.
A. put up a PROGRESS BAR... update it and .REFRESH it ... If all you want is to show that your not dead.
B. DoEvents is evil sounds A LOT like "NEVER USE A GOTO..." pleeeeze pleeeze pleeeze there are times and circumstances where any language's syntax can be harmful AND helpful. Why jump through a million hoops just to essentially do "A" above?
<soapbox>
If you know that something takes a LONG TIME and you also know that no other operations can take place WHILE YOUR WAITING (i.e. it is essentially a serial process) than if you do ANYTHING like that and push it into "the background" then you'll be sprinkling "ITS_OK_TO_CONTINUE" booleans all through the rest of your code just waiting for the file process to end anyway.... whats the point of that? All you've done is complicate your code for the sake of... hmm... "good programming?" Not in my book.
Who cares if DoEvents is "left over" from the ICE AGE. Its EXACTLY the right thing in MANY circumstances. For example: The framework gives you ProgressBar.Refresh but you'll see that its not exactly "working" unless you post-pend a few DoEvents after it.
</soapbox>
C. A background task is just that -- background; and you generally use it to operate on NON-SERIAL tasks or at least asynchronous tasks that MAY or MAY NOT update the foreground at some point. But I'd argue that anytime a so-called background task HALTS the foreground then it is (almost) by definition --- a FOREGROUND task; regardless of HOW LONG it takes.