1

I have an app that I'm working on that polls usage from an ISP (Download quota). I've tried threading this via 'new Thread(ThreaProc)' but that didn't work, now trying an IAsyncResult based approach which does the same thing... I've got no idea on how to rectify, please help?

The need-to-know:

// Global
public delegate void AsyncPollData(ref POLLDATA pData);

// Class scope:
private POLLDATA pData;

private void UpdateUsage()  
{  
  AsyncPollData PollDataProc = new AsyncPollData(frmMain.PollUsage);  
  IAsyncResult result = PollDataProc.BeginInvoke(ref pData,  
    new AsyncCallback(UpdateDone), PollDataProc);  
}

public void UpdateDone(IAsyncResult ar)
{
  AsyncPollData PollDataProc = (AsyncPollData)ar.AsyncState;
  PollDataProc.EndInvoke(ref pData, ar);
  // The Exception occurs here:
  lblStatus.Text = pData.LastError;
}

public static void PollUsage(ref POLLDATA PData)
{
  PData.LastError = "Some string";
  return;
}
Brian Rasmussen
  • 114,645
  • 34
  • 221
  • 317
Kyle
  • 11
  • 1
  • 2

5 Answers5

3
lblStatus.Invoke(delegate() { lblStatus.Text = pData.LastError; });

Updating values across threads isn't safe so the compiler warns you. By using Invoke() the passed code will be called in the GUI thread, so you're updating a GUI value in the GUI thread, which is safe.

VVS
  • 19,405
  • 5
  • 46
  • 65
3

You could create yourself a new class and create extensions like this:

public static class ThreadSafeHelpers {
        public static void SetText(this Label varLabel, string newText) {
            if (varLabel.InvokeRequired) {
                varLabel.BeginInvoke(new MethodInvoker(() => SetText(varLabel, newText)));
            } else {
                varLabel.Text = newText;
            }
        }
}

And then u use this anywhere in your code like this:

lblStatus.SetText(pData.LastError);

You can create multiple similar extensions for other things like CheckBox, RadioButtons in same class. That way you can have an easy to remember and use extension methods.

Of course you could also create a normal method like this (notice lack of this next to Label):

public static class ThreadSafeHelpers {
        public static void SetText(Label varLabel, string newText) {
            if (varLabel.InvokeRequired) {
                varLabel.BeginInvoke(new MethodInvoker(() => SetText(varLabel, newText)));
            } else {
                varLabel.Text = newText;
            }
        }
}

and use the code like this:

ThreadSafeHelpers.SetText(varLabel, newText);
MadBoy
  • 10,824
  • 24
  • 95
  • 156
1

Your control was created on thread A [The one that paints and handles windows messages] so thread B [Monitor] cannot access it [Hurray for race-conditions], take a look at this:

How to update the GUI from another thread in C#?

Cheers

Community
  • 1
  • 1
Machinarius
  • 3,637
  • 3
  • 30
  • 53
0

Thread Sleep method may be work for you

                Thread.Sleep(100);
                this.Invoke((MethodInvoker)delegate
                {

                    txtChatBox.Text += msgReceived.strMessage + "\r\n";
                });
Muhamed Shafeeq
  • 1,194
  • 1
  • 9
  • 15
0

[More notes: I'm using .NET 3.0]

Even when I use the method described in 'How to update GUI from another thread in C#?' other parts of that function (UpdateDone) fails (Even parts that are nothing to do with threading fails because certain parts of the class have been accessed outside of the current thread).

// This now works
DelegationClass.SetPropertyThreadSafe(lblStatus, () => lblStatus.Text, pData.LastError);
// Later on down the function, both objects are a member of the same class, the variable icon was never touched by another thread.
this.Icon = icon;
// An unhandled exception of type 'System.InvalidOperationException' occurred in System.Windows.Forms.dll
// Additional information: Cross-thread operation not valid: Control 'frmMain' accessed from a thread other than the thread it was created on.

Correct me if I'm wrong, but since EndInvoke is called, the thread should have terminated. Therefore no "Race conditions" should exist; all data is owned by the primary thread again and I should be free to do what I want with the data...?

There are multiple items in that struct that I use throughout the class; Is there a better way of doing this? How would you do it?

Basic constraints:
* A single function does the data polling, the idea is not to let it block UI interaction.
* Therefore that function must be Asynchronous
* The main Form class (frmMain) needs to be notified (Async) of the completion of the function in question and update it's GUI as required.
* And of course primarily, that data obtained by the function needs to be easily accessible to the members of frmMain

(God, threading was so much simpler with C++)

Litch
  • 686
  • 1
  • 7
  • 17