How make a Sub to Address a [New Thread] Call? - vb.net

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?

Related

Do I have to check the InvokeRequired and use the Invoke function with every control I want to update?

I'm writing a scheduler. It has a single form frmMain, which shows jobs that are currently running, and a history of job steps that have run. It has an object of class Scheduler that manages running new jobs. Scheduler keeps a collection class, List which contains objects of class RunningJob. RunningJob executes each step in turn through a series of sub-classes.
When a job is started, the Scheduler creates a new BackgroundWorker with the DoWork, ProgressChanged and RunWorkerCompleted methods setup with handlers that point back into the instance of RunningJob.
Each time a job/step starts/ends, one of these handers in RunningJob raises an appropriate event into Scheduler and Scheduler raises an appropriate event into frmMain. i.e.:
frmMain (1 instance) <---- Scheduler (1 instance) <---- RunningJob.WorkerProgressChanged (many instances)
The RunningJob executes correctly, but the reporting going up to the interface is not working correctly. Also any logging to files I do is suspect (I'm using a single function: LogInfo to do this). I have a number of questions:
When I use InvokeRequired() and Invoke() within frmMain, do I have to do this with every single control I want to update (there are several). Can I just check InvokeRequired() on one control and use Invoke on all of them based on that result.
Why bother checking InvokeRequired() at all and just use Invoke() every single time? It will make for simpler code.
There is only one instance of Scheduler and I am raising events to get execution back into it from each Job. I think this is part of the problem. How is multithreading handled doing this? Is there some sort of InvokeRequired/Invoke check I can do on the events before raising them? Can I raise events at all in this situation? I like events, rather than calling methods on the owner class, because it improves encapsulation. What is best practice here?
In general, if I'm calling a piece of code from many different threads, not necessarily to update a form, but just to perform some function (e.g. add a line of text to a file for logging purposes), how do I block one thread until the other has completed?

Exception: The calling thread must be STA, because many UI components require this

I'm trying to use an OCR tool. This tool gets text from PDF and converts it to RTF.
Then I have to convert it to HTML, so I use:
outputText = MarkupConverter.RtfToHtmlConverter.ConvertRtfToHtml(
System.Text.Encoding.Default.GetString(response.fileData));
I put a break point in this line, cause after this row, I got an exception of:
The calling thread must be STA, because many UI components require this.
So I tried to put <STAThread()> above the function:
<STAThread()>
Public Shared Function GetFileTextByOCRTool(path As String) As String
But it doesn't work (I get the same exception)..
Doesn't it (<STAThread()>) define the function as STA?
Any help appreciated!
If you read the STAThreadAttribute documentation you will see the following remark:
Apply this attribute to the entry point method (the Main() method in C# and Visual Basic). It has no effect on other methods. To set the apartment state of threads you start in your code, use the Thread.SetApartmentState or Thread.TrySetApartmentState method before starting the thread.
So you have to mark you application entry point using the attribute (otherwise your main thread will enter the MTA), or if you are calling the component from another thread you need to call Thread.SetApartmentState to set the thread to STA before the thread is started.
Note that once a thread uses COM interop and enters an apartment it is not possible for the thread to leave the apartment and enter another one.
If you are using task or thread pool threads you will not be able to set the apartment state to STA.

Update GUI from another class in vb.net

I'm building a server and a client for a chat that runs on Tcp and Sockets, I want the client to handle more than one connection (to servers) so I made a class called "Client" to manage the async connection, so that I can run more instances at the same time like:
Dim ConnectionToServer1 as new Client
Dim ConnectionToServer2 as new Client
Since it's async when the the "connection" receives a message it generates an event (AsyncCallback) where I can convert the received bytes into a string.
The problem is: I've tried to set this string to a RichTextBox on my Form from inside the Client class, but nothing happens, I've tried to create a delegate in the form code but nothing works, the only way I was able to put the received message in the RichTextBox is by creating a public variable called LastMessage in the Client class where the last message is stored (every time it receives a message, the sub overrides the string), and then running a thread created by the Form which keeps checking for data (since the thread has been created by the form it has access to the controls, including the RichTextBox, right?)
Although I find this a bit clunky, is there any other way (through delegates maybe?) I can do it?
Here's some code:
Client class: http://pastebin.com/GF9um8Ss
Form code: http://pastebin.com/xW7mDj8j
Sounds like you started down all the right paths.
Now, on threaded applications one of the challenges that you will face is you can have tons of worker threads, but only the main, UI thread can actually make any updates to the UI. So keeping that in mind, if you have async code that needs to update the ui you will need to use what is effectively a delegate.
You can do this using tasks these days a lot easier, so read up on the Task Parallel Library, but essentially you need a delegate/task that is marshaled to run on the ui thread to handle the UI updates.
Set this global property as false
Control.CheckForIllegalCrossThreadCalls = false
this will let you edit any control of your form from any thread

Suitable pattern for updating a progress bar from multiple threads (using TPL?)

I've been busy updating my brain with the TPL because I intend on using it for my new application (which uses .Net Framework 4.0). However I have some doubts that someone might clarify for me. Previously, I had a progress form which I would launch from the main (GUI) thread after I started the thread which needed to display its' progress. It looked something like this:
sortThread = New Thread(AddressOf _Sorter.Sort())
_ProgressForm = New FrmProgress()
_Sorter.ProgressForm = _ProgressForm
sortThread.Start()
progressForm.ShowDialog()
Basically it would initialize the thread, initialize a FrmProgress form object and assign it to the Sorter object which would then update the progress form (which contained a progress bar and some labels) from its Sort() sub on the separate thread. Updating these control properties was achieved by checking the InvokeRequired property of the FrmProgress form and if needed it would then use the Invoke() method of the control that was to be updated... ex:
Public Sub IncrementProgressBar(x As Integer)
If Me.InvokeRequired Then
pb_MainProgressBar.Invoke(Sub() IncrementProgressBar(x))
Else
pb_MainProgressBar.Increment(x)
End If
End Sub
Now I am interested in using TPL to launch separate worker threads (multiple) that may want to update the progress bar. Should I use the same pattern or should I consider accessing a public TaskScheduler.FromCurrentSynchronizationContext context that was obtained in the main GUI thread? In both cases I suppose I should provide some kind of locking mechanism on the form (SyncLock?)
Invoke should be sufficient, as you are doing. If two different threads try to invoke in parallel the first one will execute first, then the second when the UI thread becomes free. The UI thread cannot service two invokes simultaneously - they are naturally handled in FIFO sequence so there is no issue with thread safety. Any number of threads can invoke on the main thread without worrying about each other or using any additional locking mechanism.
Note, however, that any thread calling Invoke will block until the main thread can service the call. If you, for example, had many threads invoking heavy code at the same time then your various threads would block on the invoke calls until they got their kick at the can, so to speak. If you use BeginInvoke then the calling thread will simply continue executing and the invoked method will be placed in the UI thread's queue (which it will service as soon as it can).

What has happened to the thread of execution in this VB.net/COM dll?

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.