Threading Basics - c#

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
namespace testThreads
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
public void countToLots()
{
for (int i = 0; i < 10000000; i++)
{
textBox1.Text = "Counting to 10000000, value is " + i + Environment.NewLine;
}
}
public void countToZero()
{
for (int i = 10000000; i > 0; i--)
{
textBox2.Text = "Counting to 0, value is " + i + Environment.NewLine;
}
}
private void button1_Click(object sender, EventArgs e)
{
Thread countUp = new Thread(new ThreadStart(countToLots));
Thread countDown = new Thread(new ThreadStart(countToZero));
countUp.Start();
countDown.Start();
}
private void button2_Click(object sender, EventArgs e)
{
textBox3.Text = "Bobby bob bob " + Environment.NewLine;
}
}
}
I really need to try and get the hang of this - i just dont understand the theory behind why i get an error message. Could someone help me out please?
Cross-thread operation not valid:
Control 'textBox1' accessed from a
thread other than the thread it was
created on.

UI controls have "thread affinity"; they do not want to be touched by anything except the UI thread; that includes reading and writing properties. The assignment to .Text should be done from the UI thread, either by using Invoke, or BackgroundWorker.
For example:
public void countToLots()
{
for (int i = 0; i < 10000000; i++)
{
// running on bg thread
textBox1.Invoke((MethodInvoker) delegate {
// running on UI thread
textBox1.Text = "Counting to 10000000, value is "
+ i + Environment.NewLine;
});
// running on bg thread again
}
}
But note that this type of thread switching has overhead. You should not call back every iteration - you should (for example) update the UI every [n] iterations - in the above, every 10000 for example.

You cannot use a method or property of a Form control from a different thread than the thread that created (called new) the control.
To do that just do:
public void countToLots()
{
for (int i = 0; i < 10000000; i++)
{
SetText("Counting to 10000000, value is " + i + Environment.NewLine);
}
}
public void SetText(string text)
{
if (this.textBox1.InvokeRequired())
{
Action<string> auxDelegate = SetText;
this.BeginInvoke(auxDelegate,text);
}
else
{
this.textBox1.Text = text;
}
}
What the method is doing with the beginInvoke is just calling again the SetText method from the thread that created the control.

Ok, about the theory behind WHY controls have UI Thread affinity.
If you've programmed long enough you would remember the days when forms and rapid application development were not the standard. In those days just droping a control into a form was rare... everything was done by the old school.
Now, in windows, the "old school" way of doing things involved defining a WindowProc.
The WindowProc is a function which is invoked to handle application message (notice I say is, not was). This function runs on the main program thread and is in charge of handling every message the application receives, including user interface paint and refresh.
Nowadays all that is mostly automated so that when you create a form the code in charge of doing all the work is autogenerated and you don't have to worry about that... but it is still there.
Of course, if the thread in charge of drawing the user interface with all its controls, is the main thread, you'll see how changing things from another thread might disturb the application itself with race conditions and so on. In addition, since the UI handling is autogenerated you can't just put the synchronization mechanisms that you'll use with two standard threads because you only have access to code on one thread, yours, but not to the main windowproc callback.
In a way, what BeginInvoke will do for you is pass a message to the main thread telling it to kindly handle the delegate in her own context when the time is right, thus delegating the execution to the main thread.

Related

How do I handle an event in UI when it's triggered by an object in another thread without invoking a cross-thread operation exception?

I'm making my way through threading design patterns and am stuck trying to resolve a small practice project. I get a [InvalidOperationException: 'Cross-thread operation not valid: Control 'txtEventLogger' accessed from a thread other than the thread it was created on.'] when an event in my UI triggered by my object in another thread, calls a method in my UI from the UI event handler, to log some events in a textbox. How can I make this thread safe? Or am I using the wrong design pattern for handling object events when it comes to using threads?
My GUI WinForm has 3 TextBoxes and 1 button:
txtAmountOfMessages
txtMessageToSend
txtEventLogger
btnStart
I have a class named Relay, and another class derived of EventArgs CustomEventArgs for event handling.
public class Relay
{
//CustomEventArgs, derived EventArgs class to hold object index and a string message
public delegate void OnSend(object sender, CustomEventArgs e);
public event OnSend Sent;
public int index;
public int Index {get => index; set => index = value; }
public void Send(string UserMessage)
{
//TODO: Meanwhile trigger event for testing
//EventArgs class not shown, but it takes:
//CustomEventArgs Constructor CustomEventArgs(int index, string message)
Sent(this, new CustomEventArgs(this.index, UserMassage.Length.ToString()));
}
}
My WinForm code:
public partial class Form1 : Form
{
private void btnStart_Click(object sender, EventArgs e)
{
// create a couple threads
for (int i = 1; i < 3; i++)
{ // txtAmountOfMessages = 3
new Thread(() => CreateRelaysAndSendMessage(Convert.ToInt32(txtAmountOfMessages.Text), txtMessageToSend.Text)).Start();
}
}
private void CreateRelaysAndSendMessage(int AmountOfMessages, string Message)
{
List<Relay> relayList = new List<Relay>();
// Create 5 objects of Relay class, index, and subscribe to ui eventhandler
for (int i = 0; i <= 4; i++)
{
relayList.Add(new Relay());
relayList[i].Index = i;
relayList[i].Sent += RelaySent;
}
// For each object, call .Send, 3 times (from txtAmountOfMessages value)
foreach(Relay myRelay in relayList)
{
for (int j = 1; j <= AmountOfMessages; j++)
{
myRelay.Send(Message);
}
}
}
private void RelaySent(object sender, CustomEventArgs e)
{
// Exception handling error here
Log("Relay number " + e.Index.ToString() + " sent msg " + e.Message);
}
public void Log(string Message)
{
// Exception handling error here
//System.InvalidOperationException: 'Cross-thread operation not valid: Control 'txtEventLogger' accessed from a thread other than the thread it was created on.'
txtEventLogger.AppendText(Message + Environment.NewLine);
}
}
This has a relatively straight forward answer for anyone else looking.
Because you are trying to access controls on a different thread, you simply need to marshall them back by calling BeginInvoke. That's actually quite easy!
Change this :
txtEventLogger.AppendText(Message + Environment.NewLine);
To this :
txtEventLogger.BeginInvoke((Action)(() => txtEventLogger.AppendText(Message + Environment.NewLine));
Begin Invoke simply tells .NET that you want to invoke it on the UI thread (Which you should be doing, and what the error is trying to tell you).
Working with delegates can be a pain but it's part of life if you want to do multi threaded work in Windows Forms. Other options are :
Use the Windows Dispatcher (Search System.Windows.Threading.Dispatcher), although this is essentially the same thing as calling BeginInvoke on your control really.
Use a threading library that has helpers for marshalling calls. Generally these allow you to just decorate methods with an attribute to say "Hey, I want this run on the UI thread please", and it does the rest (More info here https://dotnetcoretutorials.com/2020/12/10/simplifying-multithreaded-scenarios-with-postsharp-threading/)

Correctly loading a document on a background thread

From apps I've written and one I've inherited, I have a continuing desire to better understand the thread-safety issues of loading data on a background thread. Suppose I have a simple, single-window Windows Forms app with a "Load" button and a BackgroundWorker:
The button's Click handler calls loadBackgroundWorker.RunWorkerAsync(), and the worker's DoWork handler creates and initializes an object of type Document which, after loading, it stores in the form's LoadedDocument property. In the worker's RunWorkerCompleted handler, a MessageBox displays the properties of the LoadedDocument. I know this is all hard to visualize, so I'm including complete code. Sorry that it makes the question so long to read.
Here's the form's code:
using System;
using System.ComponentModel;
using System.Windows.Forms;
namespace BackgroundLoadTest
{
public partial class Form1 : Form
{
private Document _loadedDocument;
public Document LoadedDocument
{
get
{
lock (this)
{
return _loadedDocument;
}
}
set
{
lock (this)
{
_loadedDocument = value;
}
}
}
public Form1()
{
InitializeComponent();
loadBackgroundWorker.DoWork += new DoWorkEventHandler(loadBackgroundWorker_DoWork);
loadBackgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(loadBackgroundWorker_RunWorkerCompleted);
}
void loadBackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
Document d = new Document();
d.Property1 = "Testing";
d.Property2 = 1;
d.Property3 = 2;
this.LoadedDocument = d;
}
void loadBackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
MessageBox.Show("Document loaded with Property1 = " +
LoadedDocument.Property1 + ", Property2 = " +
LoadedDocument.Property2 + ", Property3 = " +
LoadedDocument.Property3);
}
private void loadButton_Click(object sender, EventArgs e)
{
loadBackgroundWorker.RunWorkerAsync();
}
}
}
Here's the code for the Document class:
using System;
namespace BackgroundLoadTest
{
public class Document
{
public string Property1 { get; set; }
public double Property2 { get; set; }
public int Property3 { get; set; }
}
}
My question is:
What thread-safety/memory-visibility problems do you see with this code, or what would you do differently given the goal of loading data on the background thread and eventually using the loaded data on the UI thread?
Is the locking in the LoadedDocument property sufficient to ensure that data initialized in the background thread will be visible to the UI thread? Is the locking necessary? I really want to understand the seemingly very common problem of loading complex documents on a background thread while keeping the GUI responsive, and I know it's tricky stuff.
Edit: to be clear, what I'm most concerned about here is memory visibility. I want to be sure that all the data initialization done by the background thread becomes visible to the GUI thread when the worker completes. I don't want changes getting stuck in a CPU cache and remaining invisible to threads on other CPUs. I don't know how to state my concerns better because they're still rather vague to me.
Locking around your getters and setters do nothing, assigning a reference type to a variable is an atomic operation.
This is plain wrong. Locking introduces memory barriers and thereby prevents instruction reordering and makes cached values visible to other threads. Accessing fields or properties (which also access fields) from different threads without synchronization isn't guaranteed to always work and can't be considered correct code.
What you're doing is accessing the LoadedDocument property from both your background thread and your UI thread. As you have implemented locking in there, this is correct code and will be thread safe.
The DoWorkEventArgs argument in your loadBackgroundWorker_DoWork method has a Result property which should be used to set the result of the background work. The RunWorkerCompletedEventArgs.Result property then can be used to access this value. Try the following:
void loadBackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
Document d = new Document();
d.Property1 = "Testing";
d.Property2 = 1;
d.Property3 = 2;
e.Result = d;
}
void loadBackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
this.LoadedDocument = (Document)e.Result;
MessageBox.Show("Document loaded with Property1 = " +
LoadedDocument.Property1 + ", Property2 = " +
LoadedDocument.Property2 + ", Property3 = " +
LoadedDocument.Property3);
}
This tutorial is one of the most comprehensive and understandable resources in regard to multithreading in .NET which I would highly recommend. Your question would have been answered here.
Edit: Clarification of how BackgroundWorker synchronizes stuff
Still, I'm curious what magic goes on in BackgroundWorker that makes data passed via e.Result fully visible to the GUI thread.
Looking into reference source of Background worker, it is not really obvious how the result is synchronized between threads:
private void WorkerThreadStart(object argument)
{
object workerResult = null;
Exception error = null;
bool cancelled = false;
try
{
DoWorkEventArgs doWorkArgs = new DoWorkEventArgs(argument);
OnDoWork(doWorkArgs);
if (doWorkArgs.Cancel)
{
cancelled = true;
}
else
{
workerResult = doWorkArgs.Result;
}
}
catch (Exception exception)
{
error = exception;
}
RunWorkerCompletedEventArgs e =
new RunWorkerCompletedEventArgs(workerResult, error, cancelled);
asyncOperation.PostOperationCompleted(operationCompleted, e);
}
This happens on the background thread. The last line then marshals back to the UI thread. Looking further down the stack, there are no lock statements or other synchronization directives there. So how is this made thread safe?
Looking into the RunWorkerCompletedEventArgs, we find no synchronization code either. But there is some strange attribute over there:
[HostProtection(SharedState = true)]
public class RunWorkerCompletedEventArgs : AsyncCompletedEventArgs
MSDN explains:
When SharedState is true, it indicates that a state is exposed that
might be shared between threads.
So putting this attribute above your class obviously makes its members thread safe by synchronizing their access. Is this awesome? I think so. Should you use this in your code? Probably not.

How do I access variables from a different thread?

Getting error: Cross-thread operation not valid: Control 'label1' accessed from a thread other than the thread it was created on.
Code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.Windows.Forms;
namespace WindowsFormsApplication2
{
public partial class Form1 : Form
{
string CONNECTING = "Connecting to server...";
string GETTING_DATA = "Getting data...";
string CONNECT_FAIL = "Failed to connect!";
string CONNECT_SUCCESS = "Connection established!";
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
Thread t1 = new Thread(run);
t1.Start();
}
public void run() {
label1.Text = CONNECTING;
}
}
}
How do I properly fix this? I've tried using CheckForIllegalCrossThreadCalls = false; but that obviously causes errors.
I'd also like to know how I can stop the thread, since it can't be accessed anymore outside of that function.
Thanks in advance!
Try using BeginInvoke:
public void run()
{
label1.BeginInvoke(new Action(() =>
{
label1.Text = CONNECTING;
}));
}
Only the UI thread can update UI elements in a Windows Forms app. Other threads need to use BeginInvoke to update the UI.
ORGINAL: i assumed this was a WPF app and said to use this.Dispatcher, but Dispatcher isn't in Windows Forms apps.
Accessing a control from different threads
In WinForms App you can ony access directly a Control from the thread it was created.
To do such a task you will need to use InvokeRequired property of a control to see if you must use Invoke inorder to force a call of the action from the original thread.
A public method that might be accessed from any thread including the original would look like this:
public void run() {
if (label1.InvokeRequired) //Is this method being called from a different thread
this.Invoke(new MethodInvoker(()=> label1.Text = CONNECTING));
else //it's cool, this is the original thread, procceed
label1.Text = CONNECTING;
}
But if you are absolutly sure that run() method will be called only from the thread, consider not even checking if InvokeRequired and immediatly call Invoke
Further information: http://msdn.microsoft.com/en-us/library/ms171728(v=vs.80).aspx
Stopping a thread in progress
Simples is to use t1.Abort(); method of a Thread. This will throw and exception forcing it to stop where ever it was. this is great for threads that do not do any long processing so stopping it won't cause any problems.
If you do do proccesing in your thread, which means you can't just stop it in the middle then I suggest you to use a boolean that will indicate that the thread must cancel ASAP.
private bool isCancelRequired = false;
public void run() {
while(true) {
//do long processing..
if (isCancelRequired)
break;
}
}
More advanced methods: http://www.yoda.arachsys.com/csharp/threads/shutdown.shtml

Execute the last thread in threadloop

I am implementing a comic reader in C# 4.0, and browsing from one image
to the next takes some time because of some processes I implemented.
Therefore, I implemented it in such a way that the UI thread will show
first the unprocessed image whilst the background thread is processing
the image and will later replace the unprocessed image.
It all works fine but now, some users will crazily like to click next
image continusly and this causes the background worker to processes
all those clicks and display all the images.
WHAT I WANT: if a user clicks multiple times, I want the background
worker to process the last thread only.
WHAT I HAVE DONE: Now, I have implemented a function to check the
number of active threads, if the active threads is greater than 1,
background thread will not process but returns the previous image(THAT
IS NOT GOOD, because the unprocessed image will be one index ahead)
If you have idea please explain to me like a beginner!
private void button4_Click(object sender, EventArgs e)
{
Bitmap b = new Bitmap(this.CurrImage);
if (!shutdown)
{
process_updateThread = new Thread(new ThreadStart(process_update));
process_updateThread.Start();
}
pictureBox1.Image = b; //image will be replaced by worker thread image
pictureBox1.Location = ImageEdit.CalculateLocationImage(b);
SetBackColor(b);
ShowPageCount();
updateNavButtons();
}
void StopThread()
{
if(((IEnumerable)System.Diagnostics.Process.GetCurrentProcess().Threads).OfType<System.Diagnostics.ProcessThread>()
.Where(t => t.ThreadState == System.Diagnostics.ThreadState.Running).Count() > 1)
shutdown = true;
else shutdown = false;
}
I am assuming your long running process is process_update.
You must stop all running process_updates before running the next one. But DON'T USE BOOLEAN VARIABLE TO DO THAT!!! You must use synchronizing objects. Most likely it should be ManualResetEvent.
UPDATE:
This very simple example can give you an idea of multithreading and thread management
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
namespace WindowsFormsExamples
{
public partial class OnlyOneThread : Form
{
List<ManualResetEvent> threadStopEvents; //This will hold stop events for running threads
public OnlyOneThread()
{
InitializeComponent();
threadStopEvents = new List<ManualResetEvent>();
}
private void runThreadBtn_Click(object sender, EventArgs e)
{
ManualResetEvent evt = new ManualResetEvent(false);
ParameterizedThreadStart ts = new ParameterizedThreadStart(this.ThreadFunc);
Thread t = new Thread(ts);
t.Start(evt);
}
private delegate void UptadeThreadCountDelegate(); //This delegate is used by Invoke method
private void UpdateThreadCount()
{
threadcountLbl.Text = threadStopEvents.Count.ToString();
}
protected override void OnClosed(EventArgs e)
{
base.OnClosed(e);
//We must stop threads if they are still running
lock (threadStopEvents) // locking prevents simultaneous list access
{
foreach (ManualResetEvent evt in threadStopEvents)
{
evt.Set(); //signal all events
}
}
}
//This is thread function
private void ThreadFunc(Object obj)
{
ManualResetEvent stopEvent = obj as ManualResetEvent; //cast an object that was passed by Thread.Start()
lock (threadStopEvents) // locking prevents simultaneous list access
{
foreach (ManualResetEvent evt in threadStopEvents)
{
evt.Set(); //signal all events for all other threads to stop
}
threadStopEvents.Add(stopEvent); //Put our event on list
}
if (this.IsHandleCreated) // This is necessary for invocation
this.Invoke(new UptadeThreadCountDelegate(this.UpdateThreadCount)); //Invoke counter update
for (int i = 0; i < 60; i++) // this will run about 1 minute
{
if (stopEvent.WaitOne(0)) // Tests stopEvent and continues
{
//Stop signaled!!! exit!
break;
}
Thread.Sleep(1000); //Sleep 1 second
}
lock (threadStopEvents) // locking prevents simultaneous list access
{
threadStopEvents.Remove(stopEvent); //remove stop event from list
}
if (this.IsHandleCreated) // This is necessary for invocation
this.Invoke(new UptadeThreadCountDelegate(this.UpdateThreadCount)); //Invoke counter update
}
}
}
If you want to run this example you must create WindowsForms project and add Button and label on the form, then use this code to bind to those controls. Note an invocation of forms method. This is necessary when don from non-GUI threads.
I can not see an easy solution to this problem... Multithreading is never easy. Personally, I would propose following (a sort of deviation of the producer/consumer situation):
First have a general counter which signifies the currently to be rendered image (can be a simple int which is incremented for each button pressed)
An ImageMonitor which is nicely locked and has a methods to:
add an image to be rendered (with the current counter) -> this happens for each button pressed
retrieve the image which should be rendered (including the counter of the image)
Process a rendered image
Now we need a continously working Background thread which loops and in every iteration checks the ImageMonitor for the newest image to process, processes the image and returns it back to the ImageMonitor (including the counter)
When the ImageMonitor gets a rendered image from the background renderer, then it can check if the image has the correct counter value, if so then it can swap the current image with the rendered image
This solution is obviously a little complicated. However, it should work. I'm interested in other (easier) solutions.
Good luck

UploadFileAsync not asynchronous?

Aight, did a bit of Googling and searching here, the only question I found related was this, although the only answer it had wasn't marked as accepted, is old and is confusing.
My problem is basically what I've said in the title. What happens is that the GUI freezes while the upload is in progress. My code:
// stuff above snipped
public partial class Form1 : Form
{
WebClient wcUploader = new WebClient();
public Form1()
{
InitializeComponent();
wcUploader.UploadFileCompleted += new UploadFileCompletedEventHandler(UploadFileCompletedCallback);
wcUploader.UploadProgressChanged += new UploadProgressChangedEventHandler(UploadProgressCallback);
}
private void button1_Click(object sender, EventArgs e)
{
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
string toUpload = openFileDialog1.FileName;
wcUploader.UploadFileAsync(new Uri("http://anyhub.net/api/upload"), "POST", toUpload);
}
}
void UploadFileCompletedCallback(object sender, UploadFileCompletedEventArgs e)
{
textBox1.Text = System.Text.Encoding.UTF8.GetString(e.Result);
}
void UploadProgressCallback(object sender, UploadProgressChangedEventArgs e)
{
textBox1.Text = (string)e.UserState + "\n\n"
+ "Uploaded " + e.BytesSent + "/" + e.TotalBytesToSend + "b (" + e.ProgressPercentage + "%)";
}
}
EDIT: For clarification, this is what happens in order:
I click button1
I select a file
The GUI stops responding, as in when I click on it nothing happens
After a couple of seconds 50% shows up in the textbox Aaand the realisation hits. See my comment to the question I marked as the solution
After a second or so with the GUI not responding in-between it's replaced with the response
Sure it is.
The code works just fine.
wcUploader.UploadFileAsync(...) initiates the request and execution continues, meanwhile the progress is updated in TextBox1 and upon completion I get some JSON.
That is Async. If you simply called wcUploader.UploadFile, execution would block there until the file was uploaded and you would get no progress events.
Bottom line:
The UI is not blocked, progress events are called and UI is updated in real time.
Update:
To eliminate the initial block when the webclient is establishing the http connection, simply call the upload on another thread. In this scenario, you must use invocation to prevent cross thread exceptions:
using System;
using System.Net;
using System.Text;
using System.Threading;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
private readonly WebClient wcUploader = new WebClient();
public Form1()
{
InitializeComponent();
wcUploader.UploadFileCompleted += UploadFileCompletedCallback;
wcUploader.UploadProgressChanged += UploadProgressCallback;
}
private void UploadFileCompletedCallback(object sender, UploadFileCompletedEventArgs e)
{
// a clever way to handle cross-thread calls and avoid the dreaded
// "Cross-thread operation not valid: Control 'textBox1' accessed
// from a thread other than the thread it was created on." exception
// this will always be called from another thread,
// no need to check for InvokeRequired
BeginInvoke(
new MethodInvoker(() =>
{
textBox1.Text = Encoding.UTF8.GetString(e.Result);
button1.Enabled = true;
}));
}
private void UploadProgressCallback(object sender, UploadProgressChangedEventArgs e)
{
// a clever way to handle cross-thread calls and avoid the dreaded
// "Cross-thread operation not valid: Control 'textBox1' accessed
// from a thread other than the thread it was created on." exception
// this will always be called from another thread,
// no need to check for InvokeRequired
BeginInvoke(
new MethodInvoker(() =>
{
textBox1.Text = (string)e.UserState + "\n\n"
+ "Uploaded " + e.BytesSent + "/" + e.TotalBytesToSend
+ "b (" + e.ProgressPercentage + "%)";
}));
}
private void button1_Click(object sender, EventArgs e)
{
textBox1.Text = "";
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
button1.Enabled = false;
string toUpload = openFileDialog1.FileName;
textBox1.Text = "Initiating connection";
new Thread(() =>
wcUploader.UploadFileAsync(new Uri("http://anyhub.net/api/upload"), "POST", toUpload)).Start();
}
}
}
}
There is a bug in your code that's worth fixing anyhow, regardless of the UI locking:
You specify two callbacks which the asynchronous uploader should trigger. In those callbacks, you'll be running on the uploader's thread; however, you may only touch the GUI from the main GUI thread - so your callbacks might corrupt the GUI's state.
You shouldn't touch textBox1.Text in either callback. It's unlikely that's the problem, but nevertheless, you should fix it to avoid crash and corruption bugs. The question you've linked illustrates one way of avoiding this: check the form's Control.InvokeRequired property (behind the scenes this checks whether you're on the right thread), or simply assume an invoke is required and then - use Control.BeginInvoke to trigger a method on the GUI thread.
Any of your controls will do since they all run in the same thread; so if (textBox1.InvokeRequired) textBox1.BeginInvoke... is just as good as if (this.InvokeRequired) this.BeginInvoke...

Categories

Resources